diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/signin | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz |
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/signin')
121 files changed, 2189 insertions, 2280 deletions
diff --git a/chromium/components/signin/DEPS b/chromium/components/signin/DEPS index 2bd7b72d440..73fad4f9ba1 100644 --- a/chromium/components/signin/DEPS +++ b/chromium/components/signin/DEPS @@ -16,7 +16,6 @@ include_rules = [ "+google_apis/gaia", "+grit", # For generated headers "+net", - "+services/identity/public", "+services/network/public", "+services/network/test", "+sql", diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index 0234d4e1bd3..8eb46aefb22 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -21,8 +21,6 @@ static_library("browser") { "account_reconcilor_delegate.h", "chrome_connected_header_helper.cc", "chrome_connected_header_helper.h", - "consistency_cookie_manager_base.cc", - "consistency_cookie_manager_base.h", "cookie_reminter.cc", "cookie_reminter.h", "cookie_settings_util.cc", @@ -31,8 +29,6 @@ static_library("browser") { "dice_account_reconcilor_delegate.h", "dice_header_helper.cc", "dice_header_helper.h", - "mice_account_reconcilor_delegate.cc", - "mice_account_reconcilor_delegate.h", "mirror_account_reconcilor_delegate.cc", "mirror_account_reconcilor_delegate.h", "signin_error_controller.cc", @@ -93,14 +89,6 @@ static_library("browser") { ] } - if (is_android) { - sources += [ - "consistency_cookie_manager_android.cc", - "consistency_cookie_manager_android.h", - ] - deps += [ "android:jni_headers" ] - } - if (!enable_dice_support) { sources -= [ "dice_account_reconcilor_delegate.cc", @@ -117,7 +105,6 @@ source_set("unit_tests") { "account_investigator_unittest.cc", "account_reconcilor_unittest.cc", "dice_account_reconcilor_delegate_unittest.cc", - "mice_account_reconcilor_delegate_unittest.cc", "signin_error_controller_unittest.cc", "signin_header_helper_unittest.cc", "signin_investigator_unittest.cc", @@ -154,10 +141,6 @@ source_set("unit_tests") { ] } - if (is_android) { - sources += [ "consistency_cookie_manager_unittest.cc" ] - } - if (!enable_dice_support) { sources -= [ "dice_account_reconcilor_delegate_unittest.cc" ] } diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc index 67e7053c60a..ae036e28bec 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.cc +++ b/chromium/components/signin/core/browser/about_signin_internals.cc @@ -359,7 +359,7 @@ std::unique_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() { void AboutSigninInternals::OnAccessTokenRequested( const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) { + const signin::ScopeSet& scopes) { TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes); if (token) { *token = TokenInfo(consumer_id, scopes); @@ -374,7 +374,7 @@ void AboutSigninInternals::OnAccessTokenRequested( void AboutSigninInternals::OnAccessTokenRequestCompleted( const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const signin::ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) { TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes); @@ -428,7 +428,7 @@ void AboutSigninInternals::OnEndBatchOfRefreshTokenStateChanges() { void AboutSigninInternals::OnAccessTokenRemovedFromCache( const CoreAccountId& account_id, - const identity::ScopeSet& scopes) { + const signin::ScopeSet& scopes) { for (const std::unique_ptr<TokenInfo>& token : signin_status_.token_info_map[account_id]) { if (token->scopes == scopes) @@ -490,7 +490,7 @@ void AboutSigninInternals::OnAccountsInCookieUpdated( } AboutSigninInternals::TokenInfo::TokenInfo(const std::string& consumer_id, - const identity::ScopeSet& scopes) + const signin::ScopeSet& scopes) : consumer_id(consumer_id), scopes(scopes), request_time(base::Time::Now()), @@ -576,7 +576,7 @@ AboutSigninInternals::SigninStatus::~SigninStatus() {} AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken( const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) { + const signin::ScopeSet& scopes) { for (const std::unique_ptr<TokenInfo>& token : token_info_map[account_id]) { if (token->consumer_id == consumer_id && token->scopes == scopes) return token.get(); diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h index 9b34c7d9895..1b715f2dd3a 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.h +++ b/chromium/components/signin/core/browser/about_signin_internals.h @@ -21,7 +21,7 @@ #include "components/signin/core/browser/signin_internals_util.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/identity_manager/identity_manager.h" -#include "services/identity/public/cpp/scope_set.h" +#include "components/signin/public/identity_manager/scope_set.h" namespace signin { struct AccountsInCookieJarInfo; @@ -100,7 +100,7 @@ class AboutSigninInternals : public KeyedService, private: // Encapsulates diagnostic information about tokens for different services. struct TokenInfo { - TokenInfo(const std::string& consumer_id, const identity::ScopeSet& scopes); + TokenInfo(const std::string& consumer_id, const signin::ScopeSet& scopes); ~TokenInfo(); std::unique_ptr<base::DictionaryValue> ToValue() const; @@ -111,7 +111,7 @@ class AboutSigninInternals : public KeyedService, void Invalidate(); std::string consumer_id; // service that requested the token. - identity::ScopeSet scopes; // Scoped that are requested. + signin::ScopeSet scopes; // Scoped that are requested. base::Time request_time; base::Time receive_time; base::Time expiration_time; @@ -154,7 +154,7 @@ class AboutSigninInternals : public KeyedService, TokenInfo* FindToken(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes); + const signin::ScopeSet& scopes); void AddRefreshTokenEvent(const RefreshTokenEvent& event); @@ -185,14 +185,14 @@ class AboutSigninInternals : public KeyedService, // IdentityManager::DiagnosticsObserver implementations. void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) override; + const signin::ScopeSet& scopes) override; void OnAccessTokenRequestCompleted(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const signin::ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) override; void OnAccessTokenRemovedFromCache(const CoreAccountId& account_id, - const identity::ScopeSet& scopes) override; + const signin::ScopeSet& scopes) override; void OnRefreshTokenUpdatedForAccountFromSource( const CoreAccountId& account_id, bool is_refresh_token_valid, diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc index ff5b0e6982c..9d255485221 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.cc +++ b/chromium/components/signin/core/browser/account_reconcilor.cc @@ -21,7 +21,6 @@ #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "components/signin/core/browser/consistency_cookie_manager_base.h" #include "components/signin/public/base/account_consistency_method.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/base/signin_metrics.h" @@ -33,10 +32,6 @@ #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" -#if defined(OS_ANDROID) -#include "components/signin/core/browser/consistency_cookie_manager_android.h" -#endif - using signin::AccountReconcilorDelegate; using signin_metrics::AccountReconcilorState; @@ -243,6 +238,7 @@ AccountReconcilor::AccountReconcilor( error_during_last_reconcile_(GoogleServiceAuthError::AuthErrorNone()), reconcile_is_noop_(true), set_accounts_in_progress_(false), + log_out_in_progress_(false), chrome_accounts_changed_(false), account_reconcilor_lock_count_(0), reconcile_on_unblock_(false), @@ -282,17 +278,12 @@ void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) { } } -void AccountReconcilor::SetConsistencyCookieManager( - std::unique_ptr<signin::ConsistencyCookieManagerBase> - consistency_cookie_manager) { - consistency_cookie_manager_ = std::move(consistency_cookie_manager); -} - void AccountReconcilor::EnableReconcile() { - SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED); RegisterWithAllDependencies(); if (IsIdentityManagerReady()) StartReconcile(); + else + SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED); } void AccountReconcilor::DisableReconcile(bool logout_all_accounts) { @@ -455,7 +446,9 @@ void AccountReconcilor::PerformLogoutAllAccountsAction() { return; VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction"; identity_manager_->GetAccountsCookieMutator()->LogOutAllAccounts( - delegate_->GetGaiaApiSource()); + delegate_->GetGaiaApiSource(), + base::BindOnce(&AccountReconcilor::OnLogOutFromCookieCompleted, + weak_factory_.GetWeakPtr())); } void AccountReconcilor::StartReconcile() { @@ -471,6 +464,9 @@ void AccountReconcilor::StartReconcile() { return; } + // TODO(crbug.com/967603): remove when root cause is found. + CHECK(delegate_); + CHECK(client_); if (!delegate_->IsReconcileEnabled() || !client_->AreSigninCookiesAllowed()) { VLOG(1) << "AccountReconcilor::StartReconcile: !enabled or no cookies"; SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_OK); @@ -548,6 +544,7 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( // instead. PerformLogoutAllAccountsAction(); gaia_accounts.clear(); + // TODO(alexilin): Asynchronously wait until the logout is complete. OnSetAccountsInCookieCompleted( signin::SetAccountsInCookieResult::kSuccess); DCHECK(!is_reconcile_started_); @@ -761,6 +758,7 @@ void AccountReconcilor::FinishReconcile( // Really messed up state. Blow away the gaia cookie completely and // rebuild it, making sure the primary account as specified by the // IdentityManager is the first session in the gaia cookie. + log_out_in_progress_ = true; PerformLogoutAllAccountsAction(); gaia_accounts.clear(); } @@ -823,6 +821,7 @@ void AccountReconcilor::FinishReconcile( void AccountReconcilor::AbortReconcile() { VLOG(1) << "AccountReconcilor::AbortReconcile: try again later"; + log_out_in_progress_ = false; add_to_cookie_.clear(); CalculateIfReconcileIsDone(); @@ -833,7 +832,8 @@ void AccountReconcilor::AbortReconcile() { void AccountReconcilor::CalculateIfReconcileIsDone() { base::TimeDelta duration = base::Time::Now() - reconcile_start_time_; // Record the duration if reconciliation was underway and now it is over. - if (is_reconcile_started_ && add_to_cookie_.empty()) { + if (is_reconcile_started_ && add_to_cookie_.empty() && + !log_out_in_progress_) { bool was_last_reconcile_successful = (error_during_last_reconcile_.state() == GoogleServiceAuthError::State::NONE); @@ -855,7 +855,7 @@ void AccountReconcilor::CalculateIfReconcileIsDone() { } } - is_reconcile_started_ = !add_to_cookie_.empty(); + is_reconcile_started_ = !add_to_cookie_.empty() || log_out_in_progress_; if (!is_reconcile_started_) VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done"; } @@ -946,6 +946,23 @@ void AccountReconcilor::OnAddAccountToCookieCompleted( } } +void AccountReconcilor::OnLogOutFromCookieCompleted( + const GoogleServiceAuthError& error) { + VLOG(1) << "AccountReconcilor::OnLogOutFromCookieCompleted: " + << "Error was " << error.ToString(); + + if (is_reconcile_started_) { + if (error.state() != GoogleServiceAuthError::State::NONE && + !error_during_last_reconcile_.IsPersistentError()) { + error_during_last_reconcile_ = error; + } + + log_out_in_progress_ = false; + CalculateIfReconcileIsDone(); + ScheduleStartReconcileIfChromeAccountsChanged(); + } +} + void AccountReconcilor::IncrementLockCount() { DCHECK_GE(account_reconcilor_lock_count_, 0); ++account_reconcilor_lock_count_; diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h index df65ed4fe55..aa326c8c93f 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.h +++ b/chromium/components/signin/core/browser/account_reconcilor.h @@ -30,7 +30,6 @@ namespace signin { class AccountReconcilorDelegate; -class ConsistencyCookieManagerBase; enum class SetAccountsInCookieResult; } @@ -99,10 +98,6 @@ class AccountReconcilor : public KeyedService, // construction. void Initialize(bool start_reconcile_if_tokens_available); - void SetConsistencyCookieManager( - std::unique_ptr<signin::ConsistencyCookieManagerBase> - consistency_cookie_manager); - // Enables and disables the reconciliation. void EnableReconcile(); void DisableReconcile(bool logout_all_gaia_accounts); @@ -178,6 +173,8 @@ class AccountReconcilor : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, GetAccountsFromCookieSuccess); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, + EnableReconcileWhileAlreadyRunning); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, GetAccountsFromCookieFailure); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, ExtraCookieChangeNotification); @@ -287,6 +284,7 @@ class AccountReconcilor : public KeyedService, void OnAddAccountToCookieCompleted(const CoreAccountId& account_id, const GoogleServiceAuthError& error); void OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult result); + void OnLogOutFromCookieCompleted(const GoogleServiceAuthError& error); // Lock related methods. void IncrementLockCount(); @@ -346,6 +344,7 @@ class AccountReconcilor : public KeyedService, // Used during reconcile action. std::vector<CoreAccountId> add_to_cookie_; // Progress of AddAccount calls. bool set_accounts_in_progress_; // Progress of SetAccounts calls. + bool log_out_in_progress_; // Progress of LogOut calls. bool chrome_accounts_changed_; // Used for the Lock. @@ -373,9 +372,6 @@ class AccountReconcilor : public KeyedService, signin_metrics::AccountReconcilorState state_; - std::unique_ptr<signin::ConsistencyCookieManagerBase> - consistency_cookie_manager_; - base::WeakPtrFactory<AccountReconcilor> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(AccountReconcilor); diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc index 722e5733a1e..ad3a178b11a 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc @@ -21,7 +21,6 @@ #include "build/build_config.h" #include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/core/browser/mice_account_reconcilor_delegate.h" #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h" #include "components/signin/public/base/account_consistency_method.h" #include "components/signin/public/base/list_accounts_test_utils.h" @@ -144,10 +143,6 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor { bool dice_migration_completed) { switch (account_consistency) { case signin::AccountConsistencyMethod::kMirror: -#if defined(OS_ANDROID) - if (base::FeatureList::IsEnabled(signin::kMiceFeature)) - return std::make_unique<signin::MiceAccountReconcilorDelegate>(); -#endif return std::make_unique<signin::MirrorAccountReconcilorDelegate>( identity_manager); case signin::AccountConsistencyMethod::kDisabled: @@ -260,14 +255,17 @@ class AccountReconcilorTest : public ::testing::Test { const CoreAccountId& account_id, const GoogleServiceAuthError& error); - void SimulateCookieContentSettingsChanged( - content_settings::Observer* observer, - const ContentSettingsPattern& primary_pattern); - void SimulateSetAccountsInCookieCompleted( AccountReconcilor* reconcilor, signin::SetAccountsInCookieResult result); + void SimulateLogOutFromCookieCompleted(AccountReconcilor* reconcilor, + const GoogleServiceAuthError& error); + + void SimulateCookieContentSettingsChanged( + content_settings::Observer* observer, + const ContentSettingsPattern& primary_pattern); + void SetAccountConsistency(signin::AccountConsistencyMethod method); // Should never be called before |SetAccountConsistency|. @@ -292,21 +290,6 @@ class AccountReconcilorTest : public ::testing::Test { DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTest); }; -#if defined(OS_ANDROID) -// Same as AccountReconcilorTest, with Mice enabled. -class AccountReconcilorMiceTest : public AccountReconcilorTest { - public: - AccountReconcilorMiceTest() { - SetAccountConsistency(signin::AccountConsistencyMethod::kMirror); - scoped_feature_list_.InitAndEnableFeature(signin::kMiceFeature); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; - DISALLOW_COPY_AND_ASSIGN(AccountReconcilorMiceTest); -}; -#endif - class AccountReconcilorMirrorTest : public AccountReconcilorTest { public: AccountReconcilorMirrorTest() { @@ -404,6 +387,12 @@ void AccountReconcilorTest::SimulateSetAccountsInCookieCompleted( reconcilor->OnSetAccountsInCookieCompleted(result); } +void AccountReconcilorTest::SimulateLogOutFromCookieCompleted( + AccountReconcilor* reconcilor, + const GoogleServiceAuthError& error) { + reconcilor->OnLogOutFromCookieCompleted(error); +} + void AccountReconcilorTest::SimulateCookieContentSettingsChanged( content_settings::Observer* observer, const ContentSettingsPattern& primary_pattern) { @@ -695,8 +684,11 @@ class BaseAccountReconcilorTestTable : public AccountReconcilorTest { ASSERT_TRUE(reconcilor->delegate_->IsAccountConsistencyEnforced()); reconcilor->StartReconcile(); for (int i = 0; gaia_api_calls_[i] != '\0'; ++i) { - if (gaia_api_calls_[i] == 'X') + if (gaia_api_calls_[i] == 'X') { + SimulateLogOutFromCookieCompleted( + reconcilor, GoogleServiceAuthError::AuthErrorNone()); continue; + } CoreAccountId account_id = PickAccountIdForAccount(accounts_[gaia_api_calls_[i]].gaia_id, accounts_[gaia_api_calls_[i]].email); @@ -1381,6 +1373,8 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); if (!IsMultiloginEnabled()) { + SimulateLogOutFromCookieCompleted(reconcilor, + GoogleServiceAuthError::AuthErrorNone()); SimulateAddAccountToCookieCompleted( reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone()); SimulateAddAccountToCookieCompleted( @@ -1872,193 +1866,6 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(GenerateTestCasesFromParams(kActiveDirectoryParams))); #endif // defined(OS_CHROMEOS) -#if defined(OS_ANDROID) -// clang-format off -const std::vector<AccountReconcilorTestTableParam> kMiceParams = { -// This table encodes the initial state and expectations of a reconcile. -// See kDiceParams for documentation of the syntax. -// ------------------------------------------------------------------------- -// Tokens | Cookies | First Run | Gaia calls | Tokens after | Cookies after -// ------------------------------------------------------------------------- -{ "A", "A", IsFirstReconcile::kBoth, "", "A", "A"}, -{ "A", "B", IsFirstReconcile::kBoth, "U", "A", "A"}, -{ "A", "", IsFirstReconcile::kBoth, "U", "A", "A"}, -{ "A", "xA", IsFirstReconcile::kBoth, "U", "A", "A"}, -{ "A", "AxB", IsFirstReconcile::kBoth, "U", "A", "A"}, -{ "xA", "A", IsFirstReconcile::kBoth, "X", "xA", ""}, -{ "xA", "xA", IsFirstReconcile::kBoth, "", "xA", "xA"}, -{ "xA", "xB", IsFirstReconcile::kBoth, "X", "xA", ""}, -{ "xA", "", IsFirstReconcile::kBoth, "", "xA", ""}, -{ "", "A", IsFirstReconcile::kBoth, "X", "", ""}, -{ "", "xA", IsFirstReconcile::kBoth, "X", "", ""}, -}; -// clang-format on - -// Parameterized version of AccountReconcilorTest that tests Mice implementation -// with Multilogin endpoint. -class AccountReconcilorTestMiceMultilogin : public AccountReconcilorTestTable { - public: - AccountReconcilorTestMiceMultilogin() = default; - - protected: - base::test::ScopedFeatureList scoped_feature_list_; - - private: - DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestMiceMultilogin); -}; - -// Checks one row of the kMiceParams table above. -TEST_P(AccountReconcilorTestMiceMultilogin, TableRowTest) { - // Enable Mirror. - SetAccountConsistency(signin::AccountConsistencyMethod::kMirror); - scoped_feature_list_.InitAndEnableFeature(signin::kMiceFeature); - - // Setup cookies. - std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies); - ConfigureCookieManagerService(cookies); - - // Setup tokens. This triggers listing cookies so we need to setup cookies - // before that. - SetupTokens(GetParam().tokens); - - // Call list accounts now so that the next call completes synchronously. - identity_test_env()->identity_manager()->GetAccountsInCookieJar(); - base::RunLoop().RunUntilIdle(); - - // Setup expectations. - testing::InSequence mock_sequence; - bool logout_action = false; - for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) { - if (GetParam().gaia_api_calls[i] == 'X') { - logout_action = true; - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(1); - cookies.clear(); - continue; - } - if (GetParam().gaia_api_calls[i] == 'U') { - std::vector<CoreAccountId> accounts_to_send; - for (int i = 0; GetParam().cookies_after_reconcile[i] != '\0'; ++i) { - char cookie = GetParam().cookies_after_reconcile[i]; - std::string account_to_send = GaiaIdForAccountKey(cookie); - accounts_to_send.push_back(PickAccountIdForAccount( - account_to_send, - accounts_[GetParam().cookies_after_reconcile[i]].email)); - } - const signin::MultiloginParameters params( - gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, - accounts_to_send); - EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)) - .Times(1); - } - } - if (!logout_action) { - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(0); - } - - // Reconcile. - AccountReconcilor* reconcilor = GetMockReconcilor(); - ASSERT_TRUE(reconcilor); - ASSERT_TRUE(reconcilor->first_execution_); - reconcilor->first_execution_ = - GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; - reconcilor->StartReconcile(); - - SimulateSetAccountsInCookieCompleted( - reconcilor, signin::SetAccountsInCookieResult::kSuccess); - - ASSERT_FALSE(reconcilor->is_reconcile_started_); - ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); - VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile)); - - testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); - - // Another reconcile is sometimes triggered if Chrome accounts have - // changed. Allow it to finish. - EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) - .WillRepeatedly(testing::Return()); - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .WillRepeatedly(testing::Return()); - ConfigureCookieManagerService({}); - base::RunLoop().RunUntilIdle(); -} - -INSTANTIATE_TEST_SUITE_P( - MiceTableMultilogin, - AccountReconcilorTestMiceMultilogin, - ::testing::ValuesIn(GenerateTestCasesFromParams(kMiceParams))); - -// Checks that the reconcilor state does: -// RUNNING -> SCHEDULED -> RUNNING -> OK. -TEST_F(AccountReconcilorMiceTest, AccountReconcilorStateScheduled) { - class TestAccountReconcilorObserver - : public testing::StrictMock<AccountReconcilor::Observer> { - public: - MOCK_METHOD1(OnStateChanged, void(AccountReconcilorState state)); - }; - - AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); - - EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)); - - // The reconcilor should run twice without going to the OK state in between. - // OK only happens at the end. - TestAccountReconcilorObserver observer; - testing::InSequence mock_sequence; - EXPECT_CALL(observer, OnStateChanged( - AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING)) - .Times(1); - EXPECT_CALL( - observer, - OnStateChanged(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED)) - .Times(1); - EXPECT_CALL(observer, OnStateChanged( - AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING)) - .Times(1); - EXPECT_CALL(observer, - OnStateChanged(AccountReconcilorState::ACCOUNT_RECONCILOR_OK)) - .Times(1); - - AccountReconcilor* reconcilor = GetMockReconcilor(); - ASSERT_TRUE(reconcilor); - ScopedObserver<AccountReconcilor, AccountReconcilor::Observer> - scoped_observer(&observer); - scoped_observer.Add(reconcilor); - - // Reconcile was scheduled when the account was added. - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED, - reconcilor->GetState()); - - ASSERT_FALSE(reconcilor->is_reconcile_started_); - reconcilor->StartReconcile(); - ASSERT_TRUE(reconcilor->is_reconcile_started_); - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING, - reconcilor->GetState()); - base::RunLoop().RunUntilIdle(); - - // The reconcilor started a request to add the account to the cookie, and is - // waiting for the response. - - // Change the token while the reconcilor is running, to trigger another - // reconcile after the current one. - identity_test_env()->SetInvalidRefreshTokenForAccount( - account_info.account_id); - - // Unblock the first reconcile. - 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(); - - ASSERT_FALSE(reconcilor->is_reconcile_started_); - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_OK, - reconcilor->GetState()); -} -#endif // defined(OS_ANDROID) - // Tests that reconcile cannot start before the tokens are loaded, and is // automatically started when tokens are loaded. TEST_F(AccountReconcilorMirrorTest, TokensNotLoaded) { @@ -2123,6 +1930,42 @@ TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieSuccess) { ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size()); } +// Checks that calling EnableReconcile() while the reconcilor is already running +// doesn't have any effect. Regression test for https://crbug.com/1043651 +TEST_F(AccountReconcilorMirrorTest, EnableReconcileWhileAlreadyRunning) { + AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); + const CoreAccountId account_id = account_info.account_id; + signin::SetListAccountsResponseOneAccountWithParams( + {account_info.email, account_info.gaia, false /* valid */, + false /* signed_out */, true /* verified */}, + &test_url_loader_factory_); + + std::vector<CoreAccountId> accounts_to_send = {account_id}; + const signin::MultiloginParameters params( + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + accounts_to_send); + EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); + + AccountReconcilor* reconcilor = GetMockReconcilor(); + ASSERT_TRUE(reconcilor); + + ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, + reconcilor->GetState()); + reconcilor->StartReconcile(); + EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); + reconcilor->EnableReconcile(); + EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); + + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = + identity_test_env()->identity_manager()->GetAccountsInCookieJar(); + ASSERT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar_info.signed_in_accounts.size()); + ASSERT_EQ(account_id, accounts_in_cookie_jar_info.signed_in_accounts[0].id); + ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size()); +} + TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieFailure) { ConnectProfileToAccount("user@gmail.com"); signin::SetListAccountsResponseWithUnexpectedServiceResponse( @@ -2738,113 +2581,6 @@ TEST_P(AccountReconcilorMethodParamTest, ASSERT_FALSE(reconcilor->is_reconcile_started_); } -// Checks that the reconcilor state does: -// RUNNING -> SCHEDULED -> RUNNING -> OK. -// This test is similar to -// AccountReconcilorMiceTest.AccountReconctiorStateScheduled, but uses the -// MergeSession endpoint. It also uses multiple accounts, because Mirror would -// stop when the first account is in error. -TEST_P(AccountReconcilorMethodParamTest, AccountReconcilorStateScheduled) { - class TestAccountReconcilorObserver - : public testing::StrictMock<AccountReconcilor::Observer> { - public: - MOCK_METHOD1(OnStateChanged, void(AccountReconcilorState state)); - }; - - signin::AccountConsistencyMethod account_consistency = GetParam(); - SetAccountConsistency(account_consistency); - AccountInfo account_info1 = ConnectProfileToAccount("user@gmail.com"); - AccountInfo account_info2 = - identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const CoreAccountId account_id1 = account_info1.account_id; - const CoreAccountId account_id2 = account_info2.account_id; - signin::SetListAccountsResponseOneAccount( - account_info1.email, account_info1.gaia, &test_url_loader_factory_); - - AccountReconcilor* reconcilor = GetMockReconcilor(); - ASSERT_TRUE(reconcilor); - - if (!reconcilor->IsMultiloginEndpointEnabled()) { - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); - } else { - switch (account_consistency) { - case signin::AccountConsistencyMethod::kMirror: { - signin::MultiloginParameters params( - gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, - {account_id1, account_id2}); - EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - break; - } - case signin::AccountConsistencyMethod::kDice: { - signin::MultiloginParameters params( - gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, - {account_id2, account_id1}); - EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - break; - } - case signin::AccountConsistencyMethod::kDisabled: - NOTREACHED(); - break; - } - } - - // The reconcilor should run twice without going to the OK state in between. - // OK only happens at the end. - TestAccountReconcilorObserver observer; - testing::InSequence mock_sequence; - EXPECT_CALL(observer, OnStateChanged( - AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING)) - .Times(1); - EXPECT_CALL( - observer, - OnStateChanged(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED)) - .Times(1); - EXPECT_CALL(observer, OnStateChanged( - AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING)) - .Times(1); - EXPECT_CALL(observer, - OnStateChanged(AccountReconcilorState::ACCOUNT_RECONCILOR_OK)) - .Times(1); - - ScopedObserver<AccountReconcilor, AccountReconcilor::Observer> - scoped_observer(&observer); - scoped_observer.Add(reconcilor); - - // Reconcile was scheduled when the account was added. - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED, - reconcilor->GetState()); - - ASSERT_FALSE(reconcilor->is_reconcile_started_); - reconcilor->StartReconcile(); - ASSERT_TRUE(reconcilor->is_reconcile_started_); - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING, - reconcilor->GetState()); - base::RunLoop().RunUntilIdle(); - - // The reconcilor started a request to add the account to the cookie, and is - // waiting for the response. - - // Change the token while the reconcilor is running, to trigger another - // reconcile after the current one. - identity_test_env()->RemoveRefreshTokenForAccount(account_id2); - - if (!reconcilor->IsMultiloginEndpointEnabled()) { - SimulateAddAccountToCookieCompleted( - reconcilor, account_id2, GoogleServiceAuthError::AuthErrorNone()); - } else { - 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(); - - ASSERT_FALSE(reconcilor->is_reconcile_started_); - EXPECT_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_OK, - reconcilor->GetState()); -} - TEST_F(AccountReconcilorMirrorTest, AddAccountToCookieCompletedWithBogusAccount) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); diff --git a/chromium/components/signin/core/browser/android/BUILD.gn b/chromium/components/signin/core/browser/android/BUILD.gn index 15af61d18d3..62fef1b70e0 100644 --- a/chromium/components/signin/core/browser/android/BUILD.gn +++ b/chromium/components/signin/core/browser/android/BUILD.gn @@ -6,10 +6,9 @@ import("//build/config/android/rules.gni") generate_jni("jni_headers") { sources = [ - "java/src/org/chromium/components/signin/AccountManagerFacade.java", + "java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", "java/src/org/chromium/components/signin/AccountTrackerService.java", "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java", - "java/src/org/chromium/components/signin/ConsistencyCookieManager.java", ] } @@ -26,37 +25,36 @@ android_library("java") { "//ui/android:ui_java", ] - java_files = [ - "java/src/org/chromium/components/signin/AccountIdProvider.java", + sources = [ "java/src/org/chromium/components/signin/AccountManagerDelegate.java", "java/src/org/chromium/components/signin/AccountManagerDelegateException.java", "java/src/org/chromium/components/signin/AccountManagerFacade.java", + "java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", "java/src/org/chromium/components/signin/AccountManagerResult.java", - "java/src/org/chromium/components/signin/AccountsChangeObserver.java", "java/src/org/chromium/components/signin/AccountTrackerService.java", + "java/src/org/chromium/components/signin/AccountUtils.java", + "java/src/org/chromium/components/signin/AccountsChangeObserver.java", "java/src/org/chromium/components/signin/AuthException.java", "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java", "java/src/org/chromium/components/signin/ChildAccountStatus.java", "java/src/org/chromium/components/signin/ChromeSigninController.java", - "java/src/org/chromium/components/signin/ConsistencyCookieManager.java", "java/src/org/chromium/components/signin/GmsAvailabilityException.java", "java/src/org/chromium/components/signin/GmsJustUpdatedException.java", - "java/src/org/chromium/components/signin/util/PatternMatcher.java", "java/src/org/chromium/components/signin/MutableObservableValue.java", "java/src/org/chromium/components/signin/ObservableValue.java", "java/src/org/chromium/components/signin/ProfileDataSource.java", - "java/src/org/chromium/components/signin/SigninActivityMonitor.java", "java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java", + "java/src/org/chromium/components/signin/util/PatternMatcher.java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } junit_binary("components_signin_junit_tests") { - java_files = [ + sources = [ "junit/src/org/chromium/components/signin/test/AccountManagerFacadeRobolectricTest.java", - "junit/src/org/chromium/components/signin/test/PatternMatcherTest.java", "junit/src/org/chromium/components/signin/test/ObservableValueTest.java", + "junit/src/org/chromium/components/signin/test/PatternMatcherTest.java", ] deps = [ ":java", @@ -77,13 +75,14 @@ android_library("signin_javatests") { "//base:base_java", "//base:base_java_test_support", "//third_party/android_deps:androidx_annotation_annotation_java", + "//third_party/android_deps:androidx_test_monitor_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", "//third_party/jsr-305:jsr_305_javalib", "//third_party/junit", ] - java_files = [ "javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java" ] + sources = [ "javatests/src/org/chromium/components/signin/test/AccountManagerFacadeTest.java" ] } android_library("signin_java_test_support") { @@ -97,7 +96,7 @@ android_library("signin_java_test_support") { "//third_party/junit", ] - java_files = [ + sources = [ "javatests/src/org/chromium/components/signin/test/util/AccountHolder.java", "javatests/src/org/chromium/components/signin/test/util/AccountManagerTestRule.java", "javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerDelegate.java", diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java deleted file mode 100644 index d01d7b3edc6..00000000000 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountIdProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.signin; - -import androidx.annotation.VisibleForTesting; - -import com.google.android.gms.auth.GoogleAuthException; -import com.google.android.gms.auth.GoogleAuthUtil; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.StrictModeContext; -import org.chromium.base.ThreadUtils; - -import java.io.IOException; - -/** - * Returns a stable id that can be used to identify a Google Account. This - * id does not change if the email address associated to the account changes, - * nor does it change depending on whether the email has dots or varying - * capitalization. - */ -public class AccountIdProvider { - private static AccountIdProvider sProvider; - - protected AccountIdProvider() { - // should not be initialized outside getInstance(). - } - - /** - * Returns a stable id for the account associated with the given email address. - * If an account with the given email address is not installed on the device - * then null is returned. - * - * This method will throw IllegalStateException if called on the main thread. - * - * @param accountName The email address of a Google account. - */ - public String getAccountId(String accountName) { - try { - return GoogleAuthUtil.getAccountId(ContextUtils.getApplicationContext(), accountName); - } catch (IOException | GoogleAuthException ex) { - Log.e("cr.AccountIdProvider", "AccountIdProvider.getAccountId", ex); - return null; - } - } - - /** - * Returns whether the AccountIdProvider can be used. - * Since the AccountIdProvider queries Google Play services, this basically checks whether - * Google Play services is available. - */ - public boolean canBeUsed() { - // TODO(http://crbug.com/577190): Remove StrictMode override. - try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { - int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( - ContextUtils.getApplicationContext()); - return resultCode == ConnectionResult.SUCCESS; - } - } - - /** - * Gets the global account Id provider. - */ - public static AccountIdProvider getInstance() { - ThreadUtils.assertOnUiThread(); - if (sProvider == null) sProvider = new AccountIdProvider(); - return sProvider; - } - - /** - * For testing purposes only, allows to set the provider even if it has already been - * initialized. - */ - @VisibleForTesting - public static void setInstanceForTest(AccountIdProvider provider) { - ThreadUtils.assertOnUiThread(); - sProvider = provider; - } -} diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java index 279e5e5967a..3e4f343bee4 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerDelegate.java @@ -111,4 +111,22 @@ public interface AccountManagerDelegate { default ProfileDataSource getProfileDataSource() { return null; } + + /** + * Returns the Gaia id for the account associated with the given email address. + * If an account with the given email address is not installed on the device + * then null is returned. + * + * This method will throw IllegalStateException if called on the main thread. + * + * @param accountEmail The email address of a Google account. + */ + @WorkerThread + @Nullable + String getAccountGaiaId(String accountEmail); + + /** + * Checks whether Google Play services is available. + */ + boolean isGooglePlayServicesAvailable(); } diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java index 5e4157122d3..cb781dc9d38 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java @@ -29,8 +29,7 @@ import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.ObserverList; import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.metrics.CachedMetrics; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.task.AsyncTask; import org.chromium.components.signin.util.PatternMatcher; @@ -38,23 +37,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; /** * AccountManagerFacade wraps our access of AccountManager in Android. * - * Use the {@link #initializeAccountManagerFacade} to instantiate it. - * After initialization, instance get be acquired by calling {@link #get}. */ public class AccountManagerFacade { private static final String TAG = "Sync_Signin"; - private static final Pattern AT_SYMBOL = Pattern.compile("@"); - private static final String GMAIL_COM = "gmail.com"; - private static final String GOOGLEMAIL_COM = "googlemail.com"; - public static final String GOOGLE_ACCOUNT_TYPE = "com.google"; /** * An account feature (corresponding to a Gaia service flag) that specifies whether the account @@ -73,12 +64,6 @@ public class AccountManagerFacade { @VisibleForTesting public static final String ACCOUNT_RESTRICTION_PATTERNS_KEY = "RestrictAccountsToPatterns"; - private static AccountManagerFacade sInstance; - private static AccountManagerFacade sTestingInstance; - - private static final AtomicReference<AccountManagerFacade> sAtomicInstance = - new AtomicReference<>(); - private final AccountManagerDelegate mDelegate; private final ObserverList<AccountsChangeObserver> mObservers = new ObserverList<>(); @@ -89,8 +74,6 @@ public class AccountManagerFacade { private final AtomicReference<AccountManagerResult<List<Account>>> mFilteredAccounts = new AtomicReference<>(); private final CountDownLatch mPopulateAccountCacheLatch = new CountDownLatch(1); - private final CachedMetrics.TimesHistogramSample mPopulateAccountCacheWaitingTimeHistogram = - new CachedMetrics.TimesHistogramSample("Signin.AndroidPopulateAccountCacheWaitingTime"); private final ArrayList<Runnable> mCallbacksWaitingForCachePopulation = new ArrayList<>(); @@ -101,7 +84,7 @@ public class AccountManagerFacade { /** * @param delegate the AccountManagerDelegate to use as a backend */ - private AccountManagerFacade(AccountManagerDelegate delegate) { + AccountManagerFacade(AccountManagerDelegate delegate) { ThreadUtils.assertOnUiThread(); mDelegate = delegate; mDelegate.registerObservers(); @@ -115,65 +98,6 @@ public class AccountManagerFacade { } /** - * Initializes AccountManagerFacade singleton instance. Can only be called once. - * Tests can override the instance with {@link #overrideAccountManagerFacadeForTests}. - * - * @param delegate the AccountManagerDelegate to use - */ - @MainThread - public static void initializeAccountManagerFacade(AccountManagerDelegate delegate) { - ThreadUtils.assertOnUiThread(); - if (sInstance != null) { - throw new IllegalStateException("AccountManagerFacade is already initialized!"); - } - sInstance = new AccountManagerFacade(delegate); - if (sTestingInstance != null) return; - sAtomicInstance.set(sInstance); - } - - /** - * Overrides AccountManagerFacade singleton instance for tests. Only for use in Tests. - * Overrides any previous or future calls to {@link #initializeAccountManagerFacade}. - * - * @param delegate the AccountManagerDelegate to use - */ - @VisibleForTesting - @AnyThread - public static void overrideAccountManagerFacadeForTests(AccountManagerDelegate delegate) { - ThreadUtils.runOnUiThreadBlocking(() -> { - sTestingInstance = new AccountManagerFacade(delegate); - sAtomicInstance.set(sTestingInstance); - }); - } - - /** - * Resets custom AccountManagerFacade set with {@link #overrideAccountManagerFacadeForTests}. - * Only for use in Tests. - */ - @VisibleForTesting - @AnyThread - public static void resetAccountManagerFacadeForTests() { - ThreadUtils.runOnUiThreadBlocking(() -> { - sTestingInstance = null; - sAtomicInstance.set(sInstance); - }); - } - - /** - * Singleton instance getter. Singleton must be initialized before calling this by - * {@link #initializeAccountManagerFacade} or {@link #overrideAccountManagerFacadeForTests}. - * - * @return a singleton instance - */ - @AnyThread - @CalledByNative - public static AccountManagerFacade get() { - AccountManagerFacade instance = sAtomicInstance.get(); - assert instance != null : "AccountManagerFacade is not initialized!"; - return instance; - } - - /** * Adds an observer to receive accounts change notifications. * @param observer the observer to add. */ @@ -196,14 +120,6 @@ public class AccountManagerFacade { } /** - * Creates an Account object for the given name. - */ - @AnyThread - public static Account createAccountFromName(String name) { - return new Account(name, GOOGLE_ACCOUNT_TYPE); - } - - /** * Runs a callback after the account list cache is populated. In the callback * {@link #getGoogleAccounts()} and similar methods are guaranteed to return instantly (without * blocking and waiting for the cache to be populated). If the cache has already been populated, @@ -264,14 +180,6 @@ public class AccountManagerFacade { * Asynchronous version of {@link #tryGetGoogleAccountNames()}. */ @MainThread - public void tryGetGoogleAccountNames(final Callback<List<String>> callback) { - runAfterCacheIsPopulated(() -> callback.onResult(tryGetGoogleAccountNames())); - } - - /** - * Asynchronous version of {@link #tryGetGoogleAccountNames()}. - */ - @MainThread public void getGoogleAccountNames( final Callback<AccountManagerResult<List<String>>> callback) { runAfterCacheIsPopulated(() -> { @@ -306,7 +214,8 @@ public class AccountManagerFacade { mPopulateAccountCacheLatch.await(); maybeAccounts = mFilteredAccounts.get(); if (ThreadUtils.runningOnUiThread()) { - mPopulateAccountCacheWaitingTimeHistogram.record( + RecordHistogram.recordTimesHistogram( + "Signin.AndroidPopulateAccountCacheWaitingTime", SystemClock.elapsedRealtime() - now); } } catch (InterruptedException e) { @@ -317,14 +226,6 @@ public class AccountManagerFacade { } /** - * Asynchronous version of {@link #getGoogleAccounts()}. - */ - @MainThread - public void getGoogleAccounts(Callback<AccountManagerResult<List<Account>>> callback) { - runAfterCacheIsPopulated(() -> callback.onResult(mFilteredAccounts.get())); - } - - /** * Retrieves all Google accounts on the device. * Returns an empty array if an error occurs while getting account list. */ @@ -346,45 +247,15 @@ public class AccountManagerFacade { } /** - * Determine whether there are any Google accounts on the device. - * Returns false if an error occurs while getting account list. - */ - @AnyThread - public boolean hasGoogleAccounts() { - return !tryGetGoogleAccounts().isEmpty(); - } - - /** - * Asynchronous version of {@link #hasGoogleAccounts()}. - */ - @MainThread - public void hasGoogleAccounts(final Callback<Boolean> callback) { - runAfterCacheIsPopulated(() -> callback.onResult(hasGoogleAccounts())); - } - - private String canonicalizeName(String name) { - String[] parts = AT_SYMBOL.split(name); - if (parts.length != 2) return name; - - if (GOOGLEMAIL_COM.equalsIgnoreCase(parts[1])) { - parts[1] = GMAIL_COM; - } - if (GMAIL_COM.equalsIgnoreCase(parts[1])) { - parts[0] = parts[0].replace(".", ""); - } - return (parts[0] + "@" + parts[1]).toLowerCase(Locale.US); - } - - /** * Returns the account if it exists; null if account doesn't exists or an error occurs * while getting account list. */ @AnyThread public Account getAccountFromName(String accountName) { - String canonicalName = canonicalizeName(accountName); + String canonicalName = AccountUtils.canonicalizeName(accountName); List<Account> accounts = tryGetGoogleAccounts(); for (Account account : accounts) { - if (canonicalizeName(account.name).equals(canonicalName)) { + if (AccountUtils.canonicalizeName(account.name).equals(canonicalName)) { return account; } } @@ -392,14 +263,6 @@ public class AccountManagerFacade { } /** - * Asynchronous version of {@link #getAccountFromName(String)}. - */ - @MainThread - public void getAccountFromName(String accountName, final Callback<Account> callback) { - runAfterCacheIsPopulated(() -> callback.onResult(getAccountFromName(accountName))); - } - - /** * Returns whether an account exists with the given name. * Returns false if an error occurs while getting account list. */ @@ -409,23 +272,13 @@ public class AccountManagerFacade { } /** - * Asynchronous version of {@link #hasAccountForName(String)}. - */ - // TODO(maxbogue): Remove once this function is used outside of tests. - @VisibleForTesting - @MainThread - public void hasAccountForName(String accountName, final Callback<Boolean> callback) { - runAfterCacheIsPopulated(() -> callback.onResult(hasAccountForName(accountName))); - } - - /** * @return Whether or not there is an account authenticator for Google accounts. */ @AnyThread public boolean hasGoogleAccountAuthenticator() { AuthenticatorDescription[] descs = mDelegate.getAuthenticatorTypes(); for (AuthenticatorDescription desc : descs) { - if (GOOGLE_ACCOUNT_TYPE.equals(desc.type)) return true; + if (AccountUtils.GOOGLE_ACCOUNT_TYPE.equals(desc.type)) return true; } return false; } @@ -539,6 +392,28 @@ public class AccountManagerFacade { return mUpdatePendingState; } + /** + * Returns the Gaia id for the account associated with the given email address. + * If an account with the given email address is not installed on the device + * then null is returned. + * + * This method will throw IllegalStateException if called on the main thread. + * + * @param accountEmail The email address of a Google account. + */ + @WorkerThread + @Nullable + public String getAccountGaiaId(String accountEmail) { + return mDelegate.getAccountGaiaId(accountEmail); + } + + /** + * Checks whether Google Play services is available. + */ + boolean isGooglePlayServicesAvailable() { + return mDelegate.isGooglePlayServicesAvailable(); + } + private boolean hasFeature(Account account, String feature) { return mDelegate.hasFeatures(account, new String[] {feature}); } diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java new file mode 100644 index 00000000000..a9224de2e2d --- /dev/null +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java @@ -0,0 +1,85 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +package org.chromium.components.signin; + +import androidx.annotation.AnyThread; +import androidx.annotation.MainThread; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * AccountManagerFacadeProvider is intended to group all the + * AccountManagerFacade instance manipulation methods in one place. + */ +public class AccountManagerFacadeProvider { + private static final AtomicReference<AccountManagerFacade> sAtomicInstance = + new AtomicReference<>(); + private static AccountManagerFacade sInstance; + private static AccountManagerFacade sTestingInstance; + + private AccountManagerFacadeProvider() {} + + /** + * Initializes AccountManagerFacade singleton instance. Can only be called once. + * Tests can override the instance with {@link #overrideAccountManagerFacadeForTests}. + * + * @param delegate the AccountManagerDelegate to use + */ + @MainThread + public static void initializeAccountManagerFacade(AccountManagerDelegate delegate) { + ThreadUtils.assertOnUiThread(); + if (sInstance != null) { + throw new IllegalStateException("AccountManagerFacade is already initialized!"); + } + sInstance = new AccountManagerFacade(delegate); + if (sTestingInstance != null) return; + sAtomicInstance.set(sInstance); + } + + /** + * Overrides AccountManagerFacade singleton instance for tests. Only for use in Tests. + * Overrides any previous or future calls to {@link #initializeAccountManagerFacade}. + * + * @param delegate the AccountManagerDelegate to use + */ + @VisibleForTesting + @AnyThread + public static void overrideAccountManagerFacadeForTests(AccountManagerDelegate delegate) { + ThreadUtils.runOnUiThreadBlocking(() -> { + sTestingInstance = new AccountManagerFacade(delegate); + sAtomicInstance.set(sTestingInstance); + }); + } + + /** + * Resets custom AccountManagerFacade set with {@link #overrideAccountManagerFacadeForTests}. + * Only for use in Tests. + */ + @VisibleForTesting + @AnyThread + public static void resetAccountManagerFacadeForTests() { + ThreadUtils.runOnUiThreadBlocking(() -> { + sTestingInstance = null; + sAtomicInstance.set(sInstance); + }); + } + + /** + * Singleton instance getter. Singleton must be initialized before calling this by + * {@link #initializeAccountManagerFacade} or {@link #overrideAccountManagerFacadeForTests}. + * + * @return a singleton instance + */ + @AnyThread + @CalledByNative + public static AccountManagerFacade getInstance() { + AccountManagerFacade instance = sAtomicInstance.get(); + assert instance != null : "AccountManagerFacade is not initialized!"; + return instance; + } +} diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountTrackerService.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountTrackerService.java index d04128bd215..fd16e63876d 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountTrackerService.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountTrackerService.java @@ -121,9 +121,9 @@ public class AccountTrackerService { ThreadUtils.assertOnUiThread(); mSystemAccountsChanged = false; mSyncForceRefreshedForTest = false; - - final AccountIdProvider accountIdProvider = AccountIdProvider.getInstance(); - if (accountIdProvider.canBeUsed()) { + final AccountManagerFacade accountManagerFacade = + AccountManagerFacadeProvider.getInstance(); + if (accountManagerFacade.isGooglePlayServicesAvailable()) { mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_IN_PROGRESS; } else { mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_NOT_STARTED; @@ -133,10 +133,10 @@ public class AccountTrackerService { if (mAccountsChangeObserver == null) { mAccountsChangeObserver = () -> invalidateAccountSeedStatus(false /* don't reseed right now */); - AccountManagerFacade.get().addObserver(mAccountsChangeObserver); + accountManagerFacade.addObserver(mAccountsChangeObserver); } - AccountManagerFacade.get().tryGetGoogleAccounts(accounts -> { + accountManagerFacade.tryGetGoogleAccounts(accounts -> { new AsyncTask<String[][]>() { @Override public String[][] doInBackground() { @@ -147,7 +147,7 @@ public class AccountTrackerService { String[][] accountIdNameMap = new String[2][accounts.size()]; for (int i = 0; i < accounts.size(); ++i) { accountIdNameMap[0][i] = - accountIdProvider.getAccountId(accounts.get(i).name); + accountManagerFacade.getAccountGaiaId(accounts.get(i).name); accountIdNameMap[1][i] = accounts.get(i).name; } @@ -230,7 +230,7 @@ public class AccountTrackerService { } mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_VALIDATING; - AccountManagerFacade.get().tryGetGoogleAccounts(accounts -> { + AccountManagerFacadeProvider.getInstance().tryGetGoogleAccounts(accounts -> { if (mSystemAccountsChanged || mSystemAccountsSeedingStatus != SystemAccountsSeedingStatus.SEEDING_VALIDATING) { @@ -257,8 +257,8 @@ public class AccountTrackerService { @NativeMethods interface Natives { - public void seedAccountsInfo( + void seedAccountsInfo( long nativeAccountTrackerService, String[] gaiaIds, String[] accountNames); - public boolean areAccountsSeeded(long nativeAccountTrackerService, String[] accountNames); + boolean areAccountsSeeded(long nativeAccountTrackerService, String[] accountNames); } } diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountUtils.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountUtils.java new file mode 100644 index 00000000000..7723c7501f7 --- /dev/null +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountUtils.java @@ -0,0 +1,47 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.signin; + +import android.accounts.Account; + +import androidx.annotation.VisibleForTesting; + +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * AccountUtils groups some static util methods for account. + */ +public class AccountUtils { + private static final Pattern AT_SYMBOL = Pattern.compile("@"); + private static final String GMAIL_COM = "gmail.com"; + private static final String GOOGLEMAIL_COM = "googlemail.com"; + + @VisibleForTesting + public static final String GOOGLE_ACCOUNT_TYPE = "com.google"; + + /** + * Creates an Account object for the given name. + */ + public static Account createAccountFromName(String name) { + return new Account(name, GOOGLE_ACCOUNT_TYPE); + } + + /** + * Canonicalizes the account name. + */ + static String canonicalizeName(String name) { + String[] parts = AT_SYMBOL.split(name); + if (parts.length != 2) return name; + + if (GOOGLEMAIL_COM.equalsIgnoreCase(parts[1])) { + parts[1] = GMAIL_COM; + } + if (GMAIL_COM.equalsIgnoreCase(parts[1])) { + parts[0] = parts[0].replace(".", ""); + } + return (parts[0] + "@" + parts[1]).toLowerCase(Locale.US); + } +} diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java index d9382f34d5e..730e5a8a173 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java @@ -40,7 +40,7 @@ public final class ChildAccountInfoFetcher { long nativeAccountFetcherService, String accountId, String accountName) { mNativeAccountFetcherService = nativeAccountFetcherService; mAccountId = accountId; - mAccount = AccountManagerFacade.createAccountFromName(accountName); + mAccount = AccountUtils.createAccountFromName(accountName); // Register for notifications about flag changes in the future. mAccountFlagsChangedReceiver = new BroadcastReceiver() { @@ -70,7 +70,7 @@ public final class ChildAccountInfoFetcher { private void fetch() { Log.d(TAG, "Checking child account status for %s", mAccount.name); - AccountManagerFacade.get().checkChildAccountStatus( + AccountManagerFacadeProvider.getInstance().checkChildAccountStatus( mAccount, status -> setIsChildAccount(ChildAccountStatus.isChild(status))); } @@ -89,7 +89,7 @@ public final class ChildAccountInfoFetcher { @CalledByNative private static void initializeForTests() { AccountManagerDelegate delegate = new SystemAccountManagerDelegate(); - AccountManagerFacade.overrideAccountManagerFacadeForTests(delegate); + AccountManagerFacadeProvider.overrideAccountManagerFacadeForTests(delegate); } @NativeMethods diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java index 4327b1bd892..f12d1ad56e7 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java @@ -42,7 +42,7 @@ public class ChromeSigninController { if (syncAccountName == null) { return null; } - return AccountManagerFacade.createAccountFromName(syncAccountName); + return AccountUtils.createAccountFromName(syncAccountName); } public boolean isSignedIn() { diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ConsistencyCookieManager.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ConsistencyCookieManager.java deleted file mode 100644 index 44fa6beb23a..00000000000 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ConsistencyCookieManager.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.signin; - -import androidx.annotation.MainThread; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; - -/** - * Created by native code to get status of {@link AccountManagerFacade#isUpdatePending()} and - * notifications when it changes. - */ -public class ConsistencyCookieManager - implements ObservableValue.Observer, AccountTrackerService.OnSystemAccountsSeededListener { - private final long mNativeConsistencyCookieManager; - private final AccountTrackerService mAccountTrackerService; - private final AccountManagerFacade mAccountManagerFacade; - private final SigninActivityMonitor mSigninActivityMonitor; - private boolean mIsUpdatePending; - - private ConsistencyCookieManager( - long nativeConsistencyCookieManager, AccountTrackerService accountTrackerService) { - ThreadUtils.assertOnUiThread(); - mAccountTrackerService = accountTrackerService; - mNativeConsistencyCookieManager = nativeConsistencyCookieManager; - mAccountManagerFacade = AccountManagerFacade.get(); - mSigninActivityMonitor = SigninActivityMonitor.get(); - - // For now, this class relies on the order of notifications sent by AccountManagerFacade and - // AccountTrackerService. Whenever the account list changes, this class will get - // notification from AccountManagerFacade.isUpdatePending(). This will change - // mIsUpdatePending to true. By the time AccountManagerFacade finishes updating account list - // and sets AccountManagerFacade.isUpdatePending() to false, AccountTrackerService should - // have already invalidate account seed status, so mIsUpdatePending will stay false until - // accounts are seeded to the native AccountTrackerService. - // TODO(https://crbug.com/831257): Simplify this after seeding is reimplemented. - mAccountTrackerService.addSystemAccountsSeededListener(this); - mAccountManagerFacade.isUpdatePending().addObserver(this); - mSigninActivityMonitor.hasOngoingActivity().addObserver(this); - - mIsUpdatePending = calculateIsUpdatePending(); - } - - @Override - public void onSystemAccountsSeedingComplete() { - onValueChanged(); - } - - @Override - public void onValueChanged() { - boolean state = calculateIsUpdatePending(); - - if (mIsUpdatePending == state) return; - mIsUpdatePending = state; - ConsistencyCookieManagerJni.get().onIsUpdatePendingChanged(mNativeConsistencyCookieManager); - } - - private boolean calculateIsUpdatePending() { - return mAccountManagerFacade.isUpdatePending().get() - || mSigninActivityMonitor.hasOngoingActivity().get() - || !mAccountTrackerService.areSystemAccountsSeeded(); - } - - @CalledByNative - @MainThread - private static ConsistencyCookieManager create( - long nativeConsistencyCookieManager, AccountTrackerService accountTrackerService) { - return new ConsistencyCookieManager(nativeConsistencyCookieManager, accountTrackerService); - } - - @CalledByNative - @MainThread - private void destroy() { - ThreadUtils.assertOnUiThread(); - mAccountTrackerService.removeSystemAccountsSeededListener(this); - mSigninActivityMonitor.hasOngoingActivity().removeObserver(this); - mAccountManagerFacade.isUpdatePending().removeObserver(this); - } - - @CalledByNative - @MainThread - private boolean getIsUpdatePending() { - ThreadUtils.assertOnUiThread(); - return mIsUpdatePending; - } - - @JNINamespace("signin") - @NativeMethods - interface Natives { - void onIsUpdatePendingChanged(long nativeConsistencyCookieManagerAndroid); - } -} diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ObservableValue.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ObservableValue.java index 94114a89eee..99d450d8bc4 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ObservableValue.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/ObservableValue.java @@ -4,9 +4,8 @@ package org.chromium.components.signin; -import android.support.v4.util.ObjectsCompat; - import androidx.annotation.MainThread; +import androidx.core.util.ObjectsCompat; import org.chromium.base.ObserverList; import org.chromium.base.ThreadUtils; diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SigninActivityMonitor.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SigninActivityMonitor.java deleted file mode 100644 index 7fe5018d0b4..00000000000 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SigninActivityMonitor.java +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.signin; - -import android.annotation.SuppressLint; - -import androidx.annotation.MainThread; - -import org.chromium.base.ThreadUtils; - -/** - * Monitors external activities started from sign-in flows (for example, activity to add an account - * to the device). Activities have to be launched using {@link #startSigninActivity}. - */ -public class SigninActivityMonitor { - @SuppressLint("StaticFieldLeak") - private static SigninActivityMonitor sInstance; - - private int mActivityCounter; - private MutableObservableValue<Boolean> mHasOngoingActivity = - new MutableObservableValue<>(false); - - private SigninActivityMonitor() {} - - /** - * Returns a singleton instance of the SigninActivityMonitor. - */ - @MainThread - public static SigninActivityMonitor get() { - ThreadUtils.assertOnUiThread(); - if (sInstance == null) { - sInstance = new SigninActivityMonitor(); - } - return sInstance; - } - - /** - * Returns whether there are any ongoing sign-in activities. - */ - public ObservableValue<Boolean> hasOngoingActivity() { - return mHasOngoingActivity; - } - - // TODO(https://crbug.com/953765): Make this private. - /** - * Should be invoked when a signin activity is started. - */ - public void activityStarted() { - assert mActivityCounter >= 0; - - ++mActivityCounter; - if (mActivityCounter == 1) mHasOngoingActivity.set(true); - } - - // TODO(https://crbug.com/953765): Make this private. - /** - * Should be invoked when a signin activity is finished. There should be a strict parity between - * {@link #activityStarted()} and {@link #activityFinished()} calls. - */ - public void activityFinished() { - assert mActivityCounter > 0; - - --mActivityCounter; - if (mActivityCounter == 0) mHasOngoingActivity.set(false); - } -} diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java index d7220206927..4fb1f3d8f89 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java @@ -24,6 +24,8 @@ import android.os.PatternMatcher; import android.os.Process; import android.os.SystemClock; +import androidx.annotation.Nullable; + import com.google.android.gms.auth.GoogleAuthException; import com.google.android.gms.auth.GoogleAuthUtil; import com.google.android.gms.auth.GooglePlayServicesAvailabilityException; @@ -35,6 +37,7 @@ import org.chromium.base.Callback; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.ObserverList; +import org.chromium.base.StrictModeContext; import org.chromium.base.ThreadUtils; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.metrics.RecordHistogram; @@ -133,7 +136,7 @@ public class SystemAccountManagerDelegate implements AccountManagerDelegate { @Override public String getAuthToken(Account account, String authTokenScope) throws AuthException { assert !ThreadUtils.runningOnUiThread(); - assert AccountManagerFacade.GOOGLE_ACCOUNT_TYPE.equals(account.type); + assert AccountUtils.GOOGLE_ACCOUNT_TYPE.equals(account.type); try { return GoogleAuthUtil.getTokenWithNotification( ContextUtils.getApplicationContext(), account, authTokenScope, null); @@ -244,6 +247,27 @@ public class SystemAccountManagerDelegate implements AccountManagerDelegate { account, "android", emptyOptions, activity, realCallback, null); } + @Nullable + @Override + public String getAccountGaiaId(String accountEmail) { + try { + return GoogleAuthUtil.getAccountId(ContextUtils.getApplicationContext(), accountEmail); + } catch (IOException | GoogleAuthException ex) { + Log.e(TAG, "SystemAccountManagerDelegate.getAccountGaiaId", ex); + return null; + } + } + + @Override + public boolean isGooglePlayServicesAvailable() { + // TODO(http://crbug.com/577190): Remove StrictMode override. + try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { + int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( + ContextUtils.getApplicationContext()); + return resultCode == ConnectionResult.SUCCESS; + } + } + protected boolean hasGetAccountsPermission() { return ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(), Manifest.permission.GET_ACCOUNTS, Process.myPid(), Process.myUid()) diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc index ec98e9741ac..c40f6c2477e 100644 --- a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc +++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc @@ -169,12 +169,6 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader( const GURL& url, const std::string& gaia_id, int profile_mode_mask) { -#if defined(OS_ANDROID) - bool is_mice_enabled = base::FeatureList::IsEnabled(kMiceFeature); -#else - bool is_mice_enabled = false; -#endif - // If we are on mobile or desktop, an empty |account_id| corresponds to the user // not signed into Sync. Do not enforce account consistency, unless Mice is // enabled on Android. @@ -183,7 +177,7 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader( // filtered upstream and we want to enforce account consistency in Public // Sessions and Active Directory logins. #if !defined(OS_CHROMEOS) - if (gaia_id.empty() && !is_mice_enabled) + if (gaia_id.empty()) return std::string(); #endif // !defined(OS_CHROMEOS) @@ -201,9 +195,8 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader( account_consistency_ == AccountConsistencyMethod::kMirror; parts.push_back(base::StringPrintf("%s=%s", kEnableAccountConsistencyAttrName, is_mirror_enabled ? "true" : "false")); - parts.push_back(base::StringPrintf("%s=%s", - kConsistencyEnabledByDefaultAttrName, - is_mice_enabled ? "true" : "false")); + parts.push_back(base::StringPrintf( + "%s=%s", kConsistencyEnabledByDefaultAttrName, "false")); return base::JoinString(parts, is_header_request ? "," : ":"); } diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc deleted file mode 100644 index cd1f0cca6ea..00000000000 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/consistency_cookie_manager_android.h" - -#include "components/signin/core/browser/android/jni_headers/ConsistencyCookieManager_jni.h" -#include "components/signin/public/identity_manager/identity_manager.h" - -namespace signin { - -ConsistencyCookieManagerAndroid::ConsistencyCookieManagerAndroid( - IdentityManager* identity_manager, - SigninClient* signin_client, - AccountReconcilor* reconcilor) - : ConsistencyCookieManagerBase(signin_client, reconcilor) { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jobject> java_ref = - Java_ConsistencyCookieManager_create( - env, reinterpret_cast<intptr_t>(this), - identity_manager->LegacyGetAccountTrackerServiceJavaObject()); - java_ref_.Reset(env, java_ref.obj()); - is_update_pending_in_java_ = - Java_ConsistencyCookieManager_getIsUpdatePending(env, java_ref_); - - UpdateCookie(); -} - -ConsistencyCookieManagerAndroid::~ConsistencyCookieManagerAndroid() { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_ConsistencyCookieManager_destroy(env, java_ref_); -} - -void ConsistencyCookieManagerAndroid::OnIsUpdatePendingChanged(JNIEnv* env) { - bool is_update_pending_in_java = - Java_ConsistencyCookieManager_getIsUpdatePending(env, java_ref_); - if (is_update_pending_in_java == is_update_pending_in_java_) - return; - is_update_pending_in_java_ = is_update_pending_in_java; - UpdateCookie(); -} - -std::string ConsistencyCookieManagerAndroid::CalculateCookieValue() { - if (is_update_pending_in_java_) { - return kStateUpdating; - } - return ConsistencyCookieManagerBase::CalculateCookieValue(); -} - -} // namespace signin diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_android.h b/chromium/components/signin/core/browser/consistency_cookie_manager_android.h deleted file mode 100644 index 6fa0f2e2e11..00000000000 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_android.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_ - -#include "base/android/scoped_java_ref.h" -#include "base/macros.h" -#include "components/signin/core/browser/consistency_cookie_manager_base.h" - -class SigninClient; - -namespace signin { - -class IdentityManager; - -// ConsistencyCookieManagerAndroid subclasses ConsistencyCookieManagerBase to -// watch whether there are pending updates to the account list on the Java side. -class ConsistencyCookieManagerAndroid : public ConsistencyCookieManagerBase { - public: - ConsistencyCookieManagerAndroid(IdentityManager* identity_manager, - SigninClient* signin_client, - AccountReconcilor* reconcilor); - - ~ConsistencyCookieManagerAndroid() override; - - void OnIsUpdatePendingChanged(JNIEnv* env); - - protected: - std::string CalculateCookieValue() override; - - private: - bool is_update_pending_in_java_ = false; - base::android::ScopedJavaGlobalRef<jobject> java_ref_; - - DISALLOW_COPY_AND_ASSIGN(ConsistencyCookieManagerAndroid); -}; - -} // namespace signin - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_ANDROID_H_ diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc deleted file mode 100644 index 92dee2a07bc..00000000000 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/consistency_cookie_manager_base.h" - -#include "base/logging.h" -#include "base/time/time.h" -#include "components/signin/public/base/signin_client.h" -#include "google_apis/gaia/gaia_urls.h" -#include "net/cookies/canonical_cookie.h" -#include "net/cookies/cookie_options.h" -#include "services/network/public/mojom/cookie_manager.mojom.h" -#include "url/gurl.h" - -namespace signin { - -const char kCookieName[] = "CHROME_ID_CONSISTENCY_STATE"; -const char ConsistencyCookieManagerBase::kStateConsistent[] = "Consistent"; -const char ConsistencyCookieManagerBase::kStateInconsistent[] = "Inconsistent"; -const char ConsistencyCookieManagerBase::kStateUpdating[] = "Updating"; - -ConsistencyCookieManagerBase::ConsistencyCookieManagerBase( - SigninClient* signin_client, - AccountReconcilor* reconcilor) - : account_reconcilor_state_(reconcilor->GetState()), - signin_client_(signin_client), - account_reconcilor_observer_(this) { - DCHECK(signin_client_); - DCHECK(reconcilor); - - account_reconcilor_observer_.Add(reconcilor); -} - -ConsistencyCookieManagerBase::~ConsistencyCookieManagerBase() = default; - -void ConsistencyCookieManagerBase::OnStateChanged( - signin_metrics::AccountReconcilorState state) { - if (state == account_reconcilor_state_) - return; - account_reconcilor_state_ = state; - UpdateCookie(); -} - -std::string ConsistencyCookieManagerBase::CalculateCookieValue() { - switch (account_reconcilor_state_) { - case signin_metrics::ACCOUNT_RECONCILOR_OK: - return kStateConsistent; - case signin_metrics::ACCOUNT_RECONCILOR_RUNNING: - case signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED: - return kStateUpdating; - case signin_metrics::ACCOUNT_RECONCILOR_ERROR: - return kStateInconsistent; - case signin_metrics::ACCOUNT_RECONCILOR_HISTOGRAM_COUNT: - NOTREACHED(); - return {}; - } -} - -void ConsistencyCookieManagerBase::UpdateCookie() { - std::string cookie_value = CalculateCookieValue(); - DCHECK(!cookie_value.empty()); - - // Update the cookie with the new value. - network::mojom::CookieManager* cookie_manager = - signin_client_->GetCookieManager(); - base::Time now = base::Time::Now(); - base::Time expiry = now + base::TimeDelta::FromDays(2 * 365); // Two years. - net::CanonicalCookie cookie( - kCookieName, cookie_value, GaiaUrls::GetInstance()->gaia_url().host(), - /*path=*/"/", /*creation=*/now, /*expiration=*/expiry, - /*last_access=*/now, /*secure=*/true, /*httponly=*/false, - net::CookieSameSite::LAX_MODE, net::COOKIE_PRIORITY_DEFAULT); - net::CookieOptions cookie_options; - // Permit to set SameSite cookies. - cookie_options.set_same_site_cookie_context( - net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); - cookie_manager->SetCanonicalCookie( - cookie, "https", cookie_options, - network::mojom::CookieManager::SetCanonicalCookieCallback()); -} - -} // namespace signin diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_base.h b/chromium/components/signin/core/browser/consistency_cookie_manager_base.h deleted file mode 100644 index 7c1d551c4c0..00000000000 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_base.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_BASE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_BASE_H_ - -#include "base/macros.h" -#include "base/scoped_observer.h" -#include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/public/base/signin_metrics.h" - -class SigninClient; - -namespace signin { - -// The ConsistencyCookieManagerBase checks if: -// - the account reconcilor is running -// - the accounts on the device are updating -// - the user has started to interact with device account settings (from Chrome) -// If one of these conditions is true, then this object sets a cookie on Gaia -// with a "Updating" value. -// -// Otherwise the value of the cookie is "Consistent" if the accounts are -// consistent (web accounts match device accounts) or "Inconsistent". -// -// Subclasses have to call UpdateCookie() at the end of the constructor. -class ConsistencyCookieManagerBase : public AccountReconcilor::Observer { - public: - ~ConsistencyCookieManagerBase() override; - - protected: - static const char kStateConsistent[]; - static const char kStateInconsistent[]; - static const char kStateUpdating[]; - - ConsistencyCookieManagerBase(SigninClient* signin_client, - AccountReconcilor* reconcilor); - - // Calculates the cookie value solely based on the reconcilor state. - virtual std::string CalculateCookieValue(); - - // Gets the new value using CalculateCookieValue and sets the cookie. - void UpdateCookie(); - - private: - // AccountReconcilor::Observer: - void OnStateChanged(signin_metrics::AccountReconcilorState state) override; - - signin_metrics::AccountReconcilorState account_reconcilor_state_ = - signin_metrics::ACCOUNT_RECONCILOR_OK; - SigninClient* signin_client_ = nullptr; - ScopedObserver<AccountReconcilor, AccountReconcilor::Observer> - account_reconcilor_observer_; - - DISALLOW_COPY_AND_ASSIGN(ConsistencyCookieManagerBase); -}; - -} // namespace signin - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_CONSISTENCY_COOKIE_MANAGER_BASE_H_ diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc deleted file mode 100644 index b0cbd10dc9e..00000000000 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/consistency_cookie_manager_base.h" - -#include <memory> -#include <string> - -#include "base/test/scoped_feature_list.h" -#include "base/test/task_environment.h" -#include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "components/signin/public/base/account_consistency_method.h" -#include "components/signin/public/base/test_signin_client.h" -#include "components/signin/public/identity_manager/identity_test_environment.h" -#include "components/sync_preferences/testing_pref_service_syncable.h" -#include "google_apis/gaia/gaia_urls.h" -#include "services/network/test/test_cookie_manager.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace signin { -namespace { - -// GMock matcher that checks that the consistency cookie has the expected value. -MATCHER_P(CookieHasValueMatcher, value, "") { - net::CookieOptions cookie_options; - cookie_options.set_same_site_cookie_context( - net::CookieOptions::SameSiteCookieContext::SAME_SITE_LAX); - return arg.Name() == "CHROME_ID_CONSISTENCY_STATE" && arg.Value() == value && - arg.IncludeForRequestURL(GaiaUrls::GetInstance()->gaia_url(), - cookie_options) - .IsInclude(); -} - -MATCHER(SetPermittedInContext, "") { - const net::CanonicalCookie& cookie = testing::get<0>(arg); - const net::CookieOptions& cookie_options = testing::get<1>(arg); - return cookie.IsSetPermittedInContext(cookie_options).IsInclude(); -} - -class MockCookieManager - : public testing::StrictMock<network::TestCookieManager> { - public: - // Adds a GMock expectation that the consistency cookie will be set with the - // specified value. - void ExpectSetCookieCall(const std::string& value) { - EXPECT_CALL(*this, SetCanonicalCookie(CookieHasValueMatcher(value), "https", - testing::_, testing::_)) - .With(testing::Args<0, 2>(SetPermittedInContext())); - } - - MOCK_METHOD4( - SetCanonicalCookie, - void(const net::CanonicalCookie& cookie, - const std::string& source_scheme, - const net::CookieOptions& cookie_options, - network::mojom::CookieManager::SetCanonicalCookieCallback callback)); -}; - -class FakeConsistencyCookieManager : public ConsistencyCookieManagerBase { - public: - FakeConsistencyCookieManager(SigninClient* signin_client, - AccountReconcilor* reconcilor) - : ConsistencyCookieManagerBase(signin_client, reconcilor) { - UpdateCookie(); - } -}; - -class ConsistencyCookieManagerTest : public ::testing::Test { - public: - ConsistencyCookieManagerTest() - : signin_client_(&pref_service_), - identity_test_env_(/*test_url_loader_factory=*/nullptr, - &pref_service_, - AccountConsistencyMethod::kMirror, - &signin_client_) { - scoped_feature_list_.InitAndEnableFeature(kMiceFeature); - std::unique_ptr<MockCookieManager> cookie_manager = - std::make_unique<MockCookieManager>(); - mock_cookie_manager_ = cookie_manager.get(); - signin_client_.set_cookie_manager(std::move(cookie_manager)); - reconcilor_ = std::make_unique<AccountReconcilor>( - identity_test_env_.identity_manager(), &signin_client_, - std::make_unique<AccountReconcilorDelegate>()); - reconcilor_->Initialize(/*start_reconcile_if_tokens_available=*/false); - } - - ~ConsistencyCookieManagerTest() override { reconcilor_->Shutdown(); } - - SigninClient* signin_client() { return &signin_client_; } - AccountReconcilor* reconcilor() { return reconcilor_.get(); } - - MockCookieManager* mock_cookie_manager() { - DCHECK_EQ(mock_cookie_manager_, signin_client_.GetCookieManager()); - return mock_cookie_manager_; - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; - base::test::TaskEnvironment task_environment_; - sync_preferences::TestingPrefServiceSyncable pref_service_; - - // Owned by signin_client_. - MockCookieManager* mock_cookie_manager_ = nullptr; - - TestSigninClient signin_client_; - IdentityTestEnvironment identity_test_env_; - std::unique_ptr<AccountReconcilor> reconcilor_; -}; - -// Tests that the cookie is updated when the account reconcilor state changes. -TEST_F(ConsistencyCookieManagerTest, AccountReconcilorState) { - // AccountReconcilor::Initialize() creates the ConsistencyCookieManager. - mock_cookie_manager()->ExpectSetCookieCall("Consistent"); - reconcilor()->SetConsistencyCookieManager( - std::make_unique<FakeConsistencyCookieManager>(signin_client(), - reconcilor())); - testing::Mock::VerifyAndClearExpectations(mock_cookie_manager()); - ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor()->GetState()); - - // Trigger a state change in the reconcilor, and check that the cookie was - // updated accordingly. Calling EnableReconcile() will cause the reconcilor - // state to change to SCHEDULED then OK. - mock_cookie_manager()->ExpectSetCookieCall("Updating"); - mock_cookie_manager()->ExpectSetCookieCall("Consistent"); - reconcilor()->EnableReconcile(); -} - -} // namespace -} // namespace signin diff --git a/chromium/components/signin/core/browser/cookie_reminter.cc b/chromium/components/signin/core/browser/cookie_reminter.cc index d981950c1e9..cdad334c8d8 100644 --- a/chromium/components/signin/core/browser/cookie_reminter.cc +++ b/chromium/components/signin/core/browser/cookie_reminter.cc @@ -4,6 +4,7 @@ #include "components/signin/core/browser/cookie_reminter.h" +#include "base/bind_helpers.h" #include "base/metrics/histogram_macros.h" #include "base/syslog_logging.h" #include "components/signin/public/identity_manager/accounts_cookie_mutator.h" @@ -52,6 +53,7 @@ void CookieReminter::OnRefreshTokenUpdatedForAccount( // Cookies are going to be reminted for all accounts. accounts_requiring_cookie_remint_.clear(); identity_manager_->GetAccountsCookieMutator()->LogOutAllAccounts( - gaia::GaiaSource::kChromeOS); + gaia::GaiaSource::kChromeOS, + signin::AccountsCookieMutator::LogOutFromCookieCompletedCallback()); } } diff --git a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc deleted file mode 100644 index 8b8c9369672..00000000000 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/mice_account_reconcilor_delegate.h" - -#include "base/logging.h" - -namespace signin { - -MiceAccountReconcilorDelegate::MiceAccountReconcilorDelegate() = default; - -MiceAccountReconcilorDelegate::~MiceAccountReconcilorDelegate() = default; - -bool MiceAccountReconcilorDelegate::IsReconcileEnabled() const { - return true; -} - -bool MiceAccountReconcilorDelegate::IsAccountConsistencyEnforced() const { - return true; -} - -gaia::GaiaSource MiceAccountReconcilorDelegate::GetGaiaApiSource() const { - return gaia::GaiaSource::kAccountReconcilorMirror; -} - -CoreAccountId MiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( - const std::vector<CoreAccountId>& chrome_accounts, - const std::vector<gaia::ListedAccount>& gaia_accounts, - const CoreAccountId& primary_account, - bool first_execution, - bool will_logout) const { - // This flow is deprecated and will be removed when multilogin is fully - // launched. - NOTREACHED() << "Mice requires multilogin"; - return CoreAccountId(); -} - -std::vector<CoreAccountId> -MiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( - const std::vector<CoreAccountId>& chrome_accounts, - const CoreAccountId& primary_account, - const std::vector<gaia::ListedAccount>& gaia_accounts, - const gaia::MultiloginMode mode) const { - if (chrome_accounts.empty()) - return {}; - - // First account, by priority order: - // - primary account - // - first account on the device. - // Warning: As a result, the reconciliation may change the default Gaia - // account. It should be ensured that this is not surprising for the user. - CoreAccountId new_first_account = - base::Contains(chrome_accounts, primary_account) ? primary_account - : chrome_accounts[0]; - - // Minimize account shuffling and ensure that the number of accounts does not - // exceed the limit. - return ReorderChromeAccountsForReconcile(chrome_accounts, new_first_account, - gaia_accounts); -} - -gaia::MultiloginMode MiceAccountReconcilorDelegate::CalculateModeForReconcile( - const std::vector<gaia::ListedAccount>& gaia_accounts, - const CoreAccountId& primary_account, - bool first_execution, - bool primary_has_error) const { - return gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER; -} - -bool MiceAccountReconcilorDelegate::IsUnknownInvalidAccountInCookieAllowed() - const { - return false; -} - -} // namespace signin diff --git a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h deleted file mode 100644 index 28a71d00bc1..00000000000 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_MICE_ACCOUNT_RECONCILOR_DELEGATE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_MICE_ACCOUNT_RECONCILOR_DELEGATE_H_ - -#include <string> -#include <vector> - -#include "base/macros.h" -#include "components/signin/core/browser/account_reconcilor_delegate.h" - -namespace signin { - -// AccountReconcilorDelegate specialized for Mice. -class MiceAccountReconcilorDelegate : public AccountReconcilorDelegate { - public: - MiceAccountReconcilorDelegate(); - ~MiceAccountReconcilorDelegate() override; - - private: - // AccountReconcilorDelegate: - bool IsReconcileEnabled() const override; - bool IsAccountConsistencyEnforced() const override; - gaia::GaiaSource GetGaiaApiSource() const override; - CoreAccountId GetFirstGaiaAccountForReconcile( - const std::vector<CoreAccountId>& chrome_accounts, - const std::vector<gaia::ListedAccount>& gaia_accounts, - const CoreAccountId& primary_account, - bool first_execution, - bool will_logout) const override; - std::vector<CoreAccountId> GetChromeAccountsForReconcile( - const std::vector<CoreAccountId>& chrome_accounts, - const CoreAccountId& primary_account, - const std::vector<gaia::ListedAccount>& gaia_accounts, - const gaia::MultiloginMode mode) const override; - gaia::MultiloginMode CalculateModeForReconcile( - const std::vector<gaia::ListedAccount>& gaia_accounts, - const CoreAccountId& primary_account, - bool first_execution, - bool primary_has_error) const override; - bool IsUnknownInvalidAccountInCookieAllowed() const override; - - DISALLOW_COPY_AND_ASSIGN(MiceAccountReconcilorDelegate); -}; - -} // namespace signin - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_MICE_ACCOUNT_RECONCILOR_DELEGATE_H_ diff --git a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate_unittest.cc deleted file mode 100644 index b3947eaab72..00000000000 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate_unittest.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/mice_account_reconcilor_delegate.h" - -#include <vector> - -#include "google_apis/gaia/gaia_auth_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace signin { - -namespace { - -// Returns a gaia::ListedAccount with the specified account id. -gaia::ListedAccount BuildTestListedAccount(const std::string account_id, - bool valid) { - gaia::ListedAccount account; - account.id = CoreAccountId(account_id); - account.valid = valid; - return account; -} - -// Returns vector of account_id created from value -std::vector<CoreAccountId> ToAccountIdList( - const std::vector<std::string>& account_ids_value) { - std::vector<CoreAccountId> account_ids; - for (const auto& account_id_value : account_ids_value) - account_ids.push_back(CoreAccountId(account_id_value)); - return account_ids; -} - -} // namespace - -TEST(MiceAccountReconcilorDelegate, CalculateParametersForMultilogin) { - MiceAccountReconcilorDelegate mice_delegate; - - const gaia::ListedAccount kA = BuildTestListedAccount("A", /*valid=*/true); - const gaia::ListedAccount kB = BuildTestListedAccount("B", /*valid=*/false); - - struct TestParams { - std::vector<std::string> chrome_accounts; - std::string primary_account; - std::vector<gaia::ListedAccount> gaia_accounts; - - std::vector<std::string> expected_accounts; - }; - - // clang-format off - TestParams cases[] = { - // chrome_accounts, primary_account, gaia_accounts, expected_accounts - {{}, "", {}, {}}, - {{}, "", {kA, kB}, {}}, - {{"A"}, "", {}, {"A"}}, - {{"A"}, "", {kA, kB}, {"A"}}, - {{"A"}, "", {kB, kA}, {"A"}}, - {{"A", "B"}, "", {}, {"A", "B"}}, - {{"A", "B"}, "", {kA}, {"A", "B"}}, - {{"A", "B"}, "", {kB}, {"A", "B"}}, - {{"B", "C"}, "", {kA}, {"B", "C"}}, - {{"A", "B"}, "", {kA, kB}, {"A", "B"}}, - {{"A", "B"}, "", {kB, kA}, {"A", "B"}}, - {{"B", "C"}, "", {kA, kB}, {"B", "C"}}, - // Tests the reordering: B remains in 2nd place. - {{"C", "D", "B"}, "", {kA, kB}, {"C", "B", "D"}}, - // With primary account. - {{"A", "B"}, "B", {}, {"B", "A"}}, - {{"B", "A"}, "A", {kB, kA}, {"A", "B"}}, - {{"A", "B"}, "B", {kB, kA}, {"B", "A"}}, - }; - // clang-format on - - for (const auto& test : cases) { - MultiloginParameters multilogin_parameters = - mice_delegate.CalculateParametersForMultilogin( - ToAccountIdList(test.chrome_accounts), - CoreAccountId(test.primary_account), test.gaia_accounts, false, - false); - EXPECT_EQ(gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, - multilogin_parameters.mode); - EXPECT_EQ(ToAccountIdList(test.expected_accounts), - multilogin_parameters.accounts_to_send); - } -} - -} // namespace signin diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h index bc465861119..691663954a3 100644 --- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h @@ -27,6 +27,8 @@ class MirrorAccountReconcilorDelegate : public AccountReconcilorDelegate, // |ChromeOSAccountReconcilorDelegate|. bool IsReconcileEnabled() const override; + IdentityManager* GetIdentityManager() const { return identity_manager_; } + private: // AccountReconcilorDelegate: bool IsAccountConsistencyEnforced() const override; diff --git a/chromium/components/signin/core/browser/resources/signin_internals.js b/chromium/components/signin/core/browser/resources/signin_internals.js index 3f0e72974b7..c5bcefded93 100644 --- a/chromium/components/signin/core/browser/resources/signin_internals.js +++ b/chromium/components/signin/core/browser/resources/signin_internals.js @@ -16,11 +16,11 @@ chrome.signin = chrome.signin || {}; // TODO(vishwath): This function is identical to the one in sync_internals.js // Merge both if possible. -// Accepts a DOM node and sets its highlighted attribute oldVal != newVal +// Accepts a DOM node and sets its highlighted attribute oldVal !== newVal function highlightIfChanged(node, oldVal, newVal) { const oldStr = oldVal.toString(); const newStr = newVal.toString(); - if (oldStr != '' && oldStr != newStr) { + if (oldStr !== '' && oldStr !== newStr) { // Note the addListener function does not end up creating duplicate // listeners. There can be only one listener per event at a time. // Reference: https://developer.mozilla.org/en/DOM/element.addEventListener @@ -39,10 +39,10 @@ function highlightIfAnyChanged(node, oldToNewValList) { } function setClassFromValue(value) { - if (value == 0) { + if (value === 0) { return 'zero'; } - if (value == 'Successful') { + if (value === 'Successful') { return 'ok'; } @@ -68,7 +68,7 @@ Event.prototype.addListener = function(listener) { // Remove a listener from the list. Event.prototype.removeListener = function(listener) { const i = this.findListener_(listener); - if (i == -1) { + if (i === -1) { return; } this.listeners_.splice(i, 1); @@ -88,7 +88,7 @@ Event.prototype.hasListeners = function() { // Returns the index of the given listener, or -1 if not found. Event.prototype.findListener_ = function(listener) { for (let i = 0; i < this.listeners_.length; i++) { - if (this.listeners_[i] == listener) { + if (this.listeners_[i] === listener) { return i; } } diff --git a/chromium/components/signin/core/browser/signin_error_controller.h b/chromium/components/signin/core/browser/signin_error_controller.h index 77324e7e752..4ada371004a 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.h +++ b/chromium/components/signin/core/browser/signin_error_controller.h @@ -5,10 +5,8 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ERROR_CONTROLLER_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ERROR_CONTROLLER_H_ -#include <set> #include <string> -#include "base/compiler_specific.h" #include "base/macros.h" #include "base/observer_list.h" #include "base/scoped_observer.h" @@ -28,8 +26,8 @@ class SigninErrorController : public KeyedService, // are in error state, only one of the errors is reported. ANY_ACCOUNT, - // Only errors on the primary account are reported. Other accounts are - // ignored. + // Only errors on the primary account are reported. The primary account + // must have sync consent. Other accounts are ignored. PRIMARY_ACCOUNT }; diff --git a/chromium/components/signin/core/browser/signin_error_controller_unittest.cc b/chromium/components/signin/core/browser/signin_error_controller_unittest.cc index 21fa1530d82..ea955f29cd3 100644 --- a/chromium/components/signin/core/browser/signin_error_controller_unittest.cc +++ b/chromium/components/signin/core/browser/signin_error_controller_unittest.cc @@ -110,6 +110,31 @@ TEST(SigninErrorControllerTest, AccountTransitionAnyAccount) { ASSERT_FALSE(error_controller.HasError()); } +// Verifies errors are reported in mode ANY_ACCOUNT even if the primary account +// has not consented to the browser sync feature. +TEST(SigninErrorControllerTest, UnconsentedPrimaryAccount) { + base::test::TaskEnvironment task_environment; + signin::IdentityTestEnvironment identity_test_env; + + CoreAccountId test_account_id = + identity_test_env.MakeUnconsentedPrimaryAccountAvailable(kTestEmail) + .account_id; + SigninErrorController error_controller( + SigninErrorController::AccountMode::ANY_ACCOUNT, + identity_test_env.identity_manager()); + ASSERT_FALSE(error_controller.HasError()); + + identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount( + test_account_id, + GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + EXPECT_TRUE(error_controller.HasError()); + EXPECT_EQ(test_account_id, error_controller.error_account_id()); + + identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount( + test_account_id, GoogleServiceAuthError::AuthErrorNone()); + EXPECT_FALSE(error_controller.HasError()); +} + // This test exercises behavior on signin/signout, which is not relevant on // ChromeOS. #if !defined(OS_CHROMEOS) diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h index 20c4e4dcb8e..0c9cdf7c709 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.h +++ b/chromium/components/signin/core/browser/signin_header_helper.h @@ -43,13 +43,16 @@ extern const char kDiceResponseHeader[]; // perform. // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.signin +// NOTE: This enum is persisted to histograms. Do not change or reorder +// values. enum GAIAServiceType : int { GAIA_SERVICE_TYPE_NONE = 0, // No Gaia response header. GAIA_SERVICE_TYPE_SIGNOUT, // Logout all existing sessions. GAIA_SERVICE_TYPE_INCOGNITO, // Open an incognito tab. - GAIA_SERVICE_TYPE_ADDSESSION, // Add a secondary account. + GAIA_SERVICE_TYPE_ADDSESSION, // Add or re-authenticate an account. GAIA_SERVICE_TYPE_SIGNUP, // Create a new account. GAIA_SERVICE_TYPE_DEFAULT, // All other cases. + kMaxValue = GAIA_SERVICE_TYPE_DEFAULT }; enum class DiceAction { 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 1dcb4c5352c..2f6e6a338b7 100644 --- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc @@ -142,31 +142,12 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdChromeOS) { // Tests that no Mirror request is returned when the user is not signed in (no // account id), for non Chrome OS platforms. TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestNoAccountId) { -#if defined(OS_ANDROID) - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndDisableFeature(kMiceFeature); -#endif account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "", ""); CheckMirrorCookieRequest(GURL("https://docs.google.com"), "", ""); } #endif -#if defined(OS_ANDROID) -// Tests that Mirror request is returned on Android with Mice. -TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdMice) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature(kMiceFeature); - account_consistency_ = AccountConsistencyMethod::kMirror; - CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "", - "mode=0,enable_account_consistency=true," - "consistency_enabled_by_default=true"); - CheckMirrorCookieRequest(GURL("https://docs.google.com"), "", - "mode=0:enable_account_consistency=true:" - "consistency_enabled_by_default=true"); -} -#endif - // Tests that no Mirror request is returned when the cookies aren't allowed to // be set. TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) { diff --git a/chromium/components/signin/internal/base/DEPS b/chromium/components/signin/internal/base/DEPS index c079a1bff2c..5fa21919cc3 100644 --- a/chromium/components/signin/internal/base/DEPS +++ b/chromium/components/signin/internal/base/DEPS @@ -3,6 +3,6 @@ include_rules = [ specific_include_rules = { "account_manager_facade_android.cc": [ - "+components/signin/core/browser/android/jni_headers/AccountManagerFacade_jni.h", + "+components/signin/core/browser/android/jni_headers/AccountManagerFacadeProvider_jni.h", ], } diff --git a/chromium/components/signin/internal/base/account_manager_facade_android.cc b/chromium/components/signin/internal/base/account_manager_facade_android.cc index 83ca3fc562e..2cd58170d6b 100644 --- a/chromium/components/signin/internal/base/account_manager_facade_android.cc +++ b/chromium/components/signin/internal/base/account_manager_facade_android.cc @@ -4,9 +4,10 @@ #include "components/signin/internal/base/account_manager_facade_android.h" -#include "components/signin/core/browser/android/jni_headers/AccountManagerFacade_jni.h" +#include "components/signin/core/browser/android/jni_headers/AccountManagerFacadeProvider_jni.h" base::android::ScopedJavaLocalRef<jobject> AccountManagerFacadeAndroid::GetJavaObject() { - return Java_AccountManagerFacade_get(base::android::AttachCurrentThread()); + return Java_AccountManagerFacadeProvider_getInstance( + base::android::AttachCurrentThread()); } diff --git a/chromium/components/signin/internal/identity_manager/BUILD.gn b/chromium/components/signin/internal/identity_manager/BUILD.gn index fa402c9bc87..e7d0ccee66f 100644 --- a/chromium/components/signin/internal/identity_manager/BUILD.gn +++ b/chromium/components/signin/internal/identity_manager/BUILD.gn @@ -24,8 +24,6 @@ source_set("identity_manager") { "diagnostics_provider_impl.h", "gaia_cookie_manager_service.cc", "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", @@ -41,6 +39,8 @@ source_set("identity_manager") { "profile_oauth2_token_service_builder.h", "profile_oauth2_token_service_delegate.cc", "profile_oauth2_token_service_delegate.h", + "profile_oauth2_token_service_delegate_android.cc", + "profile_oauth2_token_service_delegate_android.h", "profile_oauth2_token_service_delegate_chromeos.cc", "profile_oauth2_token_service_delegate_chromeos.h", "profile_oauth2_token_service_delegate_ios.h", @@ -73,9 +73,9 @@ source_set("identity_manager") { if (is_android) { deps += [ - "android:jni_headers", "//components/signin/core/browser/android:jni_headers", "//components/signin/internal/base", + "//components/signin/public/android:jni_headers", ] } @@ -130,11 +130,11 @@ source_set("unit_tests") { "account_info_util_unittest.cc", "account_tracker_service_unittest.cc", "gaia_cookie_manager_service_unittest.cc", - "oauth2_token_service_delegate_android_unittest.cc", "oauth_multilogin_helper_unittest.cc", "oauth_multilogin_token_fetcher_unittest.cc", "primary_account_manager_unittest.cc", "primary_account_policy_manager_impl_unittest.cc", + "profile_oauth2_token_service_delegate_android_unittest.cc", "profile_oauth2_token_service_delegate_chromeos_unittest.cc", "profile_oauth2_token_service_delegate_ios_unittest.mm", "profile_oauth2_token_service_delegate_unittest.cc", diff --git a/chromium/components/signin/internal/identity_manager/DEPS b/chromium/components/signin/internal/identity_manager/DEPS index c621843ba1e..0381e12029d 100644 --- a/chromium/components/signin/internal/identity_manager/DEPS +++ b/chromium/components/signin/internal/identity_manager/DEPS @@ -14,4 +14,7 @@ specific_include_rules = { "child_account_info_fetcher_android.cc": [ "+components/signin/core/browser/android/jni_headers/ChildAccountInfoFetcher_jni.h", ], + "profile_oauth2_token_service_delegate_android.cc": [ + "+components/signin/public/android/jni_headers/ProfileOAuth2TokenServiceDelegate_jni.h", + ] } diff --git a/chromium/components/signin/internal/identity_manager/account_fetcher_service.cc b/chromium/components/signin/internal/identity_manager/account_fetcher_service.cc index b68852ce51f..39dbbb3fb5c 100644 --- a/chromium/components/signin/internal/identity_manager/account_fetcher_service.cc +++ b/chromium/components/signin/internal/identity_manager/account_fetcher_service.cc @@ -23,6 +23,7 @@ #include "components/signin/public/base/avatar_icon_util.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/base/signin_switches.h" +#include "net/http/http_status_code.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #if defined(OS_ANDROID) @@ -74,8 +75,11 @@ void AccountFetcherService::Initialize( DCHECK(image_decoder); DCHECK(!image_decoder_); image_decoder_ = std::move(image_decoder); - last_updated_ = signin_client_->GetPrefs()->GetTime( - AccountFetcherService::kLastUpdatePref); + repeating_timer_ = std::make_unique<signin::PersistentRepeatingTimer>( + signin_client_->GetPrefs(), AccountFetcherService::kLastUpdatePref, + kRefreshFromTokenServiceDelay, + base::Bind(&AccountFetcherService::RefreshAllAccountInfo, + base::Unretained(this), false)); // Tokens may have already been loaded and we will not receive a // notification-on-registration for |token_service_->AddObserver(this)| few @@ -161,7 +165,7 @@ void AccountFetcherService::MaybeEnableNetworkFetches() { return; if (!network_fetches_enabled_) { network_fetches_enabled_ = true; - ScheduleNextRefresh(); + repeating_timer_->Start(); } RefreshAllAccountInfo(true); #if defined(OS_ANDROID) @@ -169,29 +173,6 @@ void AccountFetcherService::MaybeEnableNetworkFetches() { #endif } -void AccountFetcherService::RefreshAllAccountsAndScheduleNext() { - DCHECK(network_fetches_enabled_); - RefreshAllAccountInfo(false); - last_updated_ = base::Time::Now(); - signin_client_->GetPrefs()->SetTime(AccountFetcherService::kLastUpdatePref, - last_updated_); - ScheduleNextRefresh(); -} - -void AccountFetcherService::ScheduleNextRefresh() { - DCHECK(!timer_.IsRunning()); - DCHECK(network_fetches_enabled_); - - const base::TimeDelta time_since_update = base::Time::Now() - last_updated_; - if (time_since_update > kRefreshFromTokenServiceDelay) { - RefreshAllAccountsAndScheduleNext(); - } else { - timer_.Start(FROM_HERE, kRefreshFromTokenServiceDelay - time_since_update, - this, - &AccountFetcherService::RefreshAllAccountsAndScheduleNext); - } -} - // Starts fetching user information. This is called periodically to refresh. void AccountFetcherService::StartFetchingUserInfo( const CoreAccountId& account_id) { @@ -273,13 +254,23 @@ AccountFetcherService::GetOrCreateImageFetcher() { void AccountFetcherService::FetchAccountImage(const CoreAccountId& account_id) { DCHECK(signin_client_); - std::string picture_url_string = - account_tracker_service_->GetAccountInfo(account_id).picture_url; + AccountInfo account_info = + account_tracker_service_->GetAccountInfo(account_id); + std::string picture_url_string = account_info.picture_url; + GURL picture_url(picture_url_string); if (!picture_url.is_valid()) { DVLOG(1) << "Invalid avatar picture URL: \"" + picture_url_string + "\""; return; } + GURL image_url_with_size(signin::GetAvatarImageURLWithOptions( + picture_url, signin::kAccountInfoImageSize, true /* no_silhouette */)); + + if (image_url_with_size.spec() == + account_info.last_downloaded_image_url_with_size) { + return; + } + net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("accounts_image_fetcher", R"( semantics { @@ -302,14 +293,14 @@ void AccountFetcherService::FetchAccountImage(const CoreAccountId& account_id) { "uploaded or saved; this request merely downloads the web account" "profile image." })"); - GURL image_url_with_size(signin::GetAvatarImageURLWithOptions( - picture_url, signin::kAccountInfoImageSize, true /* no_silhouette */)); - auto callback = base::BindRepeating(&AccountFetcherService::OnImageFetched, - base::Unretained(this), account_id); + + auto callback = base::BindOnce(&AccountFetcherService::OnImageFetched, + base::Unretained(this), account_id, + image_url_with_size.spec()); image_fetcher::ImageFetcherParams params(traffic_annotation, kImageFetcherUmaClient); - GetOrCreateImageFetcher()->FetchImage(image_url_with_size, callback, - std::move(params)); + GetOrCreateImageFetcher()->FetchImage(image_url_with_size, + std::move(callback), std::move(params)); } void AccountFetcherService::OnUserInfoFetchFailure( @@ -368,7 +359,13 @@ void AccountFetcherService::OnRefreshTokensLoaded() { void AccountFetcherService::OnImageFetched( const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image, - const image_fetcher::RequestMetadata&) { - account_tracker_service_->SetAccountImage(account_id, image); + const image_fetcher::RequestMetadata& metadata) { + if (metadata.http_response_code != net::HTTP_OK) { + DCHECK(image.IsEmpty()); + return; + } + account_tracker_service_->SetAccountImage(account_id, image_url_with_size, + image); } diff --git a/chromium/components/signin/internal/identity_manager/account_fetcher_service.h b/chromium/components/signin/internal/identity_manager/account_fetcher_service.h index 6e22e2fd24a..b827ba002b7 100644 --- a/chromium/components/signin/internal/identity_manager/account_fetcher_service.h +++ b/chromium/components/signin/internal/identity_manager/account_fetcher_service.h @@ -16,6 +16,7 @@ #include "base/timer/timer.h" #include "build/build_config.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_observer.h" +#include "components/signin/public/base/persistent_repeating_timer.h" class AccountInfoFetcher; class AccountTrackerService; @@ -100,8 +101,6 @@ class AccountFetcherService : public ProfileOAuth2TokenServiceObserver { friend class AccountInfoFetcher; void RefreshAllAccountInfo(bool only_fetch_if_invalid); - void RefreshAllAccountsAndScheduleNext(); - void ScheduleNextRefresh(); #if defined(OS_ANDROID) // Called on all account state changes. Decides whether to fetch new child @@ -138,6 +137,7 @@ class AccountFetcherService : public ProfileOAuth2TokenServiceObserver { void FetchAccountImage(const CoreAccountId& account_id); void OnImageFetched(const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image, const image_fetcher::RequestMetadata& image_metadata); @@ -149,8 +149,7 @@ class AccountFetcherService : public ProfileOAuth2TokenServiceObserver { bool refresh_tokens_loaded_ = false; bool shutdown_called_ = false; bool enable_account_removal_for_test_ = false; - base::Time last_updated_; - base::OneShotTimer timer_; + std::unique_ptr<signin::PersistentRepeatingTimer> repeating_timer_; #if defined(OS_ANDROID) CoreAccountId child_request_account_id_; diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc index 413b94d26e1..aec0f43225b 100644 --- a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -17,6 +18,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" #include "base/task/task_traits.h" +#include "base/task/thread_pool.h" #include "base/task_runner_util.h" #include "base/threading/scoped_blocking_call.h" #include "base/trace_event/trace_event.h" @@ -25,6 +27,7 @@ #include "components/prefs/scoped_user_pref_update.h" #include "components/signin/internal/identity_manager/account_info_util.h" #include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/signin_switches.h" #include "ui/gfx/image/image.h" #if defined(OS_ANDROID) @@ -33,7 +36,6 @@ #endif namespace { - const char kAccountKeyPath[] = "account_id"; const char kAccountEmailPath[] = "email"; const char kAccountGaiaPath[] = "gaia"; @@ -42,6 +44,8 @@ const char kAccountFullNamePath[] = "full_name"; const char kAccountGivenNamePath[] = "given_name"; const char kAccountLocalePath[] = "locale"; const char kAccountPictureURLPath[] = "picture_url"; +const char kLastDownloadedImageURLWithSizePath[] = + "last_downloaded_image_url_with_size"; const char kAccountChildAccountStatusPath[] = "is_child_account"; const char kAdvancedProtectionAccountStatusPath[] = "is_under_advanced_protection"; @@ -70,7 +74,7 @@ gfx::Image ReadImage(const base::FilePath& image_path) { } // Saves |png_data| to disk at |image_path|. -void SaveImage(scoped_refptr<base::RefCountedMemory> png_data, +bool SaveImage(scoped_refptr<base::RefCountedMemory> png_data, const base::FilePath& image_path) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); @@ -78,12 +82,14 @@ void SaveImage(scoped_refptr<base::RefCountedMemory> png_data, base::FilePath dir = image_path.DirName(); if (!base::DirectoryExists(dir) && !base::CreateDirectory(dir)) { LOG(ERROR) << "Failed to create parent directory of: " << image_path; - return; + return false; } if (base::WriteFile(image_path, png_data->front_as<char>(), png_data->size()) == -1) { LOG(ERROR) << "Failed to save image to file: " << image_path; + return false; } + return true; } // Removes the image at path |image_path|. @@ -124,8 +130,8 @@ void AccountTrackerService::Initialize(PrefService* pref_service, if (!user_data_dir_.empty()) { // |image_storage_task_runner_| is a sequenced runner because we want to // avoid read and write operations to the same file at the same time. - image_storage_task_runner_ = base::CreateSequencedTaskRunner( - {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE, + image_storage_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); LoadAccountImagesFromDisk(); } @@ -183,7 +189,7 @@ AccountInfo AccountTrackerService::FindAccountInfoByEmail( // static bool AccountTrackerService::IsMigrationSupported() { #if defined(OS_CHROMEOS) - return false; + return base::FeatureList::IsEnabled(switches::kAccountIdMigration); #else return true; #endif @@ -261,19 +267,22 @@ void AccountTrackerService::SetAccountInfoFromUserInfo( SaveToPrefs(account_info); } -void AccountTrackerService::SetAccountImage(const CoreAccountId& account_id, - const gfx::Image& image) { +void AccountTrackerService::SetAccountImage( + const CoreAccountId& account_id, + const std::string& image_url_with_size, + const gfx::Image& image) { if (!base::Contains(accounts_, account_id)) return; AccountInfo& account_info = accounts_[account_id]; account_info.account_image = image; - SaveAccountImageToDisk(account_id, image); + account_info.last_downloaded_image_url_with_size = image_url_with_size; + SaveAccountImageToDisk(account_id, image, image_url_with_size); NotifyAccountUpdated(account_info); } void AccountTrackerService::SetIsChildAccount(const CoreAccountId& account_id, bool is_child_account) { - DCHECK(base::Contains(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)) << account_id.ToString(); AccountInfo& account_info = accounts_[account_id]; if (account_info.is_child_account == is_child_account) return; @@ -286,7 +295,7 @@ void AccountTrackerService::SetIsChildAccount(const CoreAccountId& account_id, void AccountTrackerService::SetIsAdvancedProtectionAccount( const CoreAccountId& account_id, bool is_under_advanced_protection) { - DCHECK(base::Contains(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)) << account_id.ToString(); AccountInfo& account_info = accounts_[account_id]; if (account_info.is_under_advanced_protection == is_under_advanced_protection) return; @@ -308,6 +317,10 @@ void AccountTrackerService::SetOnAccountRemovedCallback( on_account_removed_callback_ = callback; } +void AccountTrackerService::CommitPendingAccountChanges() { + pref_service_->CommitPendingWrite(); +} + void AccountTrackerService::MigrateToGaiaId() { DCHECK_EQ(GetMigrationState(), MIGRATION_IN_PROGRESS); @@ -411,6 +424,10 @@ void AccountTrackerService::OnAccountImageLoaded( accounts_[account_id].account_image.IsEmpty()) { AccountInfo& account_info = accounts_[account_id]; account_info.account_image = image; + if (account_info.account_image.IsEmpty()) { + account_info.last_downloaded_image_url_with_size = std::string(); + OnAccountImageUpdated(account_id, std::string(), true); + } NotifyAccountUpdated(account_info); } } @@ -430,12 +447,42 @@ void AccountTrackerService::LoadAccountImagesFromDisk() { void AccountTrackerService::SaveAccountImageToDisk( const CoreAccountId& account_id, - const gfx::Image& image) { + const gfx::Image& image, + const std::string& image_url_with_size) { if (!image_storage_task_runner_) return; - image_storage_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&SaveImage, image.As1xPNGBytes(), - GetImagePathFor(account_id))); + + PostTaskAndReplyWithResult( + image_storage_task_runner_.get(), FROM_HERE, + base::BindOnce(&SaveImage, image.As1xPNGBytes(), + GetImagePathFor(account_id)), + base::BindOnce(&AccountTrackerService::OnAccountImageUpdated, + weak_factory_.GetWeakPtr(), account_id, + image_url_with_size)); +} + +void AccountTrackerService::OnAccountImageUpdated( + const CoreAccountId& account_id, + const std::string& image_url_with_size, + bool success) { + if (!success || !pref_service_) + return; + + base::DictionaryValue* dict = nullptr; + ListPrefUpdate update(pref_service_, prefs::kAccountInfo); + for (size_t i = 0; i < update->GetSize(); ++i, dict = nullptr) { + if (update->GetDictionary(i, &dict)) { + std::string value; + if (dict->GetString(kAccountKeyPath, &value) && + value == account_id.ToString()) + break; + } + } + + if (!dict) { + return; + } + dict->SetString(kLastDownloadedImageURLWithSizePath, image_url_with_size); } void AccountTrackerService::RemoveAccountImageFromDisk( @@ -479,6 +526,8 @@ void AccountTrackerService::LoadFromPrefs() { account_info.locale = value; if (dict->GetString(kAccountPictureURLPath, &value)) account_info.picture_url = value; + if (dict->GetString(kLastDownloadedImageURLWithSizePath, &value)) + account_info.last_downloaded_image_url_with_size = value; bool is_child_account = false; if (dict->GetBoolean(kAccountChildAccountStatusPath, &is_child_account)) @@ -564,6 +613,9 @@ void AccountTrackerService::SaveToPrefs(const AccountInfo& account_info) { account_info.is_child_account); dict->SetBoolean(kAdvancedProtectionAccountStatusPath, account_info.is_under_advanced_protection); + // |kLastDownloadedImageURLWithSizePath| should only be set after the GAIA + // picture is successufly saved to disk. Otherwise, there is no guarantee that + // |kLastDownloadedImageURLWithSizePath| matches the picture on disk. } void AccountTrackerService::RemoveFromPrefs(const AccountInfo& account_info) { @@ -640,6 +692,13 @@ CoreAccountId AccountTrackerService::SeedAccountInfo(AccountInfo info) { SaveToPrefs(account_info); } + + if (!already_exists && !info.account_image.IsEmpty()) { + SetAccountImage(account_info.account_id, + account_info.last_downloaded_image_url_with_size, + info.account_image); + } + return info.account_id; } diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service.h b/chromium/components/signin/internal/identity_manager/account_tracker_service.h index 5ba22774038..e6f29106fd4 100644 --- a/chromium/components/signin/internal/identity_manager/account_tracker_service.h +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.h @@ -18,6 +18,7 @@ #include "base/sequence_checker.h" #include "base/timer/timer.h" #include "build/build_config.h" +#include "components/prefs/scoped_user_pref_update.h" #include "components/signin/public/identity_manager/account_info.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -50,6 +51,7 @@ void SimulateSuccessfulFetchOfAccountInfo(IdentityManager*, const std::string&); void SimulateAccountImageFetch(signin::IdentityManager*, const CoreAccountId&, + const std::string& image_url_with_size, const gfx::Image&); } // namespace signin @@ -150,6 +152,10 @@ class AccountTrackerService { // valid GaiaId gets removed from |accounts_| (i.e. stops being tracked). void SetOnAccountRemovedCallback(AccountInfoCallback callback); + // Flushes the account changes to disk. The flush happens asynchronously and + // this function does not block on disk IO. + void CommitPendingAccountChanges(); + protected: // Available to be called in tests. void SetAccountInfoFromUserInfo(const CoreAccountId& account_id, @@ -158,6 +164,7 @@ class AccountTrackerService { // Updates the account image. Does nothing if |account_id| does not exist in // |accounts_|. void SetAccountImage(const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image); private: @@ -174,6 +181,7 @@ class AccountTrackerService { const std::string&); friend void signin::SimulateAccountImageFetch(signin::IdentityManager*, const CoreAccountId&, + const std::string&, const gfx::Image&); void NotifyAccountUpdated(const AccountInfo& account_info); @@ -192,7 +200,11 @@ class AccountTrackerService { void OnAccountImageLoaded(const CoreAccountId& account_id, gfx::Image image); void LoadAccountImagesFromDisk(); void SaveAccountImageToDisk(const CoreAccountId& account_id, - const gfx::Image& image); + const gfx::Image& image, + const std::string& image_url_with_size); + void OnAccountImageUpdated(const CoreAccountId& account_id, + const std::string& image_url_with_size, + bool success); void RemoveAccountImageFromDisk(const CoreAccountId& account_id); // Migrate accounts to be keyed by gaia id instead of normalized email. diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc index 72550c91a18..839d60231ce 100644 --- a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc @@ -444,18 +444,21 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageSuccess) { AccountKeyToEmail(kAccountKeyAlpha)), })); - EXPECT_TRUE(account_tracker() - ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) - .account_image.IsEmpty()); + AccountInfo account_info = account_tracker()->GetAccountInfo( + AccountKeyToAccountId(kAccountKeyAlpha)); + EXPECT_TRUE(account_info.account_image.IsEmpty()); + EXPECT_TRUE(account_info.last_downloaded_image_url_with_size.empty()); ReturnAccountImageFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), })); - EXPECT_FALSE(account_tracker() - ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) - .account_image.IsEmpty()); + account_info = account_tracker()->GetAccountInfo( + AccountKeyToAccountId(kAccountKeyAlpha)); + EXPECT_FALSE(account_info.account_image.IsEmpty()); + EXPECT_EQ(account_info.last_downloaded_image_url_with_size, + AccountKeyToPictureURLWithSize(kAccountKeyAlpha)); } TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageFailure) { @@ -468,13 +471,15 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageFailure) { AccountKeyToEmail(kAccountKeyAlpha)), })); - EXPECT_TRUE(account_tracker() - ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) - .account_image.IsEmpty()); + AccountInfo account_info = account_tracker()->GetAccountInfo( + AccountKeyToAccountId(kAccountKeyAlpha)); + EXPECT_TRUE(account_info.account_image.IsEmpty()); + EXPECT_TRUE(account_info.last_downloaded_image_url_with_size.empty()); ReturnAccountImageFetchFailure(kAccountKeyAlpha); - EXPECT_TRUE(account_tracker() - ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) - .account_image.IsEmpty()); + account_info = account_tracker()->GetAccountInfo( + AccountKeyToAccountId(kAccountKeyAlpha)); + EXPECT_TRUE(account_info.account_image.IsEmpty()); + EXPECT_TRUE(account_info.last_downloaded_image_url_with_size.empty()); } TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_Revoked) { diff --git a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc index 5c769973d83..28a8a1f89c2 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc @@ -10,21 +10,35 @@ #include "components/signin/internal/identity_manager/account_tracker_service.h" #include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" #include "components/signin/public/base/multilogin_parameters.h" +#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/google_service_auth_error.h" namespace signin { +AccountsCookieMutatorImpl::MultiloginHelperWrapper::MultiloginHelperWrapper( + std::unique_ptr<OAuthMultiloginHelper> helper) + : helper_(std::move(helper)) {} + +AccountsCookieMutatorImpl::MultiloginHelperWrapper::~MultiloginHelperWrapper() = + default; + AccountsCookieMutatorImpl::AccountsCookieMutatorImpl( + SigninClient* signin_client, + ProfileOAuth2TokenService* token_service, GaiaCookieManagerService* gaia_cookie_manager_service, AccountTrackerService* account_tracker_service) - : gaia_cookie_manager_service_(gaia_cookie_manager_service), + : signin_client_(signin_client), + token_service_(token_service), + gaia_cookie_manager_service_(gaia_cookie_manager_service), account_tracker_service_(account_tracker_service) { + DCHECK(signin_client_); + DCHECK(token_service_); DCHECK(gaia_cookie_manager_service_); DCHECK(account_tracker_service_); } -AccountsCookieMutatorImpl::~AccountsCookieMutatorImpl() {} +AccountsCookieMutatorImpl::~AccountsCookieMutatorImpl() = default; void AccountsCookieMutatorImpl::AddAccountToCookie( const CoreAccountId& account_id, @@ -58,6 +72,32 @@ void AccountsCookieMutatorImpl::SetAccountsInCookie( std::move(set_accounts_in_cookies_completed_callback)); } +std::unique_ptr<AccountsCookieMutator::SetAccountsInCookieTask> +AccountsCookieMutatorImpl::SetAccountsInCookieForPartition( + PartitionDelegate* partition_delegate, + const MultiloginParameters& parameters, + base::OnceCallback<void(SetAccountsInCookieResult)> + set_accounts_in_cookies_completed_callback) { + // The default partition must go through the GaiaCookieManagerService. + DCHECK_NE(signin_client_->GetCookieManager(), + partition_delegate->GetCookieManagerForPartition()) + << "The default partition is passed to " + << "SetAccountsInCookieForPartition(). Use SetAccountsInCookie() " + << "instead."; + + std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> accounts; + for (const auto& account_id : parameters.accounts_to_send) { + accounts.push_back(make_pair( + account_id, account_tracker_service_->GetAccountInfo(account_id).gaia)); + } + + return std::make_unique<MultiloginHelperWrapper>( + std::make_unique<OAuthMultiloginHelper>( + signin_client_, partition_delegate, token_service_, parameters.mode, + accounts, /*external_cc_result=*/std::string(), + std::move(set_accounts_in_cookies_completed_callback))); +} + void AccountsCookieMutatorImpl::TriggerCookieJarUpdate() { gaia_cookie_manager_service_->TriggerListAccounts(); } @@ -68,8 +108,11 @@ void AccountsCookieMutatorImpl::ForceTriggerOnCookieChange() { } #endif -void AccountsCookieMutatorImpl::LogOutAllAccounts(gaia::GaiaSource source) { - gaia_cookie_manager_service_->LogOutAllAccounts(source); +void AccountsCookieMutatorImpl::LogOutAllAccounts( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback completion_callback) { + gaia_cookie_manager_service_->LogOutAllAccounts( + source, std::move(completion_callback)); } } // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h index f363638b3e7..1f4d5f56a74 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h @@ -8,12 +8,16 @@ #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "build/build_config.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_helper.h" #include "components/signin/public/identity_manager/accounts_cookie_mutator.h" class AccountTrackerService; class GaiaCookieManagerService; +class ProfileOAuth2TokenService; +class SigninClient; namespace gaia { class GaiaSource; @@ -25,6 +29,8 @@ namespace signin { class AccountsCookieMutatorImpl : public AccountsCookieMutator { public: explicit AccountsCookieMutatorImpl( + SigninClient* signin_client, + ProfileOAuth2TokenService* token_service, GaiaCookieManagerService* gaia_cookie_manager_service, AccountTrackerService* account_tracker_service); ~AccountsCookieMutatorImpl() override; @@ -46,15 +52,34 @@ class AccountsCookieMutatorImpl : public AccountsCookieMutator { base::OnceCallback<void(SetAccountsInCookieResult)> set_accounts_in_cookies_completed_callback) override; + std::unique_ptr<SetAccountsInCookieTask> SetAccountsInCookieForPartition( + PartitionDelegate* partition_delegate, + const MultiloginParameters& parameters, + base::OnceCallback<void(SetAccountsInCookieResult)> + set_accounts_in_cookies_completed_callback) override; + void TriggerCookieJarUpdate() override; #if defined(OS_IOS) void ForceTriggerOnCookieChange() override; #endif - void LogOutAllAccounts(gaia::GaiaSource source) override; + void LogOutAllAccounts( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback completion_callback) override; private: + class MultiloginHelperWrapper : public SetAccountsInCookieTask { + public: + MultiloginHelperWrapper(std::unique_ptr<OAuthMultiloginHelper> helper); + ~MultiloginHelperWrapper() override; + + private: + std::unique_ptr<OAuthMultiloginHelper> helper_; + }; + + SigninClient* signin_client_; + ProfileOAuth2TokenService* token_service_; GaiaCookieManagerService* gaia_cookie_manager_service_; AccountTrackerService* account_tracker_service_; diff --git a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc index 6fa9e606e6d..a61eab0fec3 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc @@ -46,6 +46,12 @@ CoreAccountId AccountsMutatorImpl::AddOrUpdateAccount( account_tracker_service_->SeedAccountInfo(gaia_id, email); account_tracker_service_->SetIsAdvancedProtectionAccount( account_id, is_under_advanced_protection); + + // Flush the account changes to disk. Otherwise, in case of a browser crash, + // the account may be added to the token service but not to the account + // tracker, which is not intended. + account_tracker_service_->CommitPendingAccountChanges(); + token_service_->UpdateCredentials(account_id, refresh_token, source); return account_id; diff --git a/chromium/components/signin/internal/identity_manager/android/BUILD.gn b/chromium/components/signin/internal/identity_manager/android/BUILD.gn deleted file mode 100644 index a851e12d7a1..00000000000 --- a/chromium/components/signin/internal/identity_manager/android/BUILD.gn +++ /dev/null @@ -1,12 +0,0 @@ -import("//build/config/android/rules.gni") - -generate_jni("jni_headers") { - namespace = "signin" - sources = [ - "//components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountId.java", - "//components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountInfo.java", - "//components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", - "//components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", - "//components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/OAuth2TokenService.java", - ] -} diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc index 7d1e64b1cc6..f01b138d04d 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc @@ -163,6 +163,12 @@ void GaiaCookieManagerService::GaiaCookieRequest:: std::move(add_account_to_cookie_completed_callback_).Run(account_id, error); } +void GaiaCookieManagerService::GaiaCookieRequest:: + RunLogOutFromCookieCompletedCallback(const GoogleServiceAuthError& error) { + if (log_out_from_cookie_completed_callback_) + std::move(log_out_from_cookie_completed_callback_).Run(error); +} + // static GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateAddAccountRequest( @@ -194,9 +200,12 @@ GaiaCookieManagerService::GaiaCookieRequest::CreateSetAccountsRequest( // static GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateLogOutRequest( - gaia::GaiaSource source) { - return GaiaCookieManagerService::GaiaCookieRequest( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback callback) { + GaiaCookieManagerService::GaiaCookieRequest request( GaiaCookieRequestType::LOG_OUT, source); + request.log_out_from_cookie_completed_callback_ = std::move(callback); + return request; } // static @@ -598,7 +607,9 @@ void GaiaCookieManagerService::ForceOnCookieChangeProcessing() { net::CookieChangeCause::UNKNOWN_DELETION)); } -void GaiaCookieManagerService::LogOutAllAccounts(gaia::GaiaSource source) { +void GaiaCookieManagerService::LogOutAllAccounts( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback completion_callback) { VLOG(1) << "GaiaCookieManagerService::LogOutAllAccounts"; bool log_out_queued = false; @@ -638,13 +649,17 @@ void GaiaCookieManagerService::LogOutAllAccounts(gaia::GaiaSource source) { } if (!log_out_queued) { - requests_.push_back(GaiaCookieRequest::CreateLogOutRequest(source)); + requests_.push_back(GaiaCookieRequest::CreateLogOutRequest( + source, std::move(completion_callback))); if (requests_.size() == 1) { fetcher_retries_ = 0; signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut, weak_ptr_factory_.GetWeakPtr())); } + } else { + std::move(completion_callback) + .Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); } } @@ -741,6 +756,12 @@ void GaiaCookieManagerService::SignalSetAccountsComplete( requests_.front().RunSetAccountsInCookieCompletedCallback(result); } +void GaiaCookieManagerService::SignalLogOutComplete( + const GoogleServiceAuthError& error) { + DCHECK(requests_.front().request_type() == GaiaCookieRequestType::LOG_OUT); + requests_.front().RunLogOutFromCookieCompletedCallback(error); +} + void GaiaCookieManagerService::SetGaiaAccountsInCookieUpdatedCallback( GaiaAccountsInCookieUpdatedCallback callback) { DCHECK(!gaia_accounts_updated_in_cookie_callback_); @@ -910,6 +931,7 @@ void GaiaCookieManagerService::OnLogOutSuccess() { RecordLogoutRequestState(LogoutRequestState::kSuccess); MarkListAccountsStale(); + SignalLogOutComplete(GoogleServiceAuthError::AuthErrorNone()); fetcher_backoff_.InformOfRequest(true); HandleNextRequest(); } @@ -931,9 +953,22 @@ void GaiaCookieManagerService::OnLogOutFailure( return; } + SignalLogOutComplete(error); HandleNextRequest(); } +std::unique_ptr<GaiaAuthFetcher> +GaiaCookieManagerService::CreateGaiaAuthFetcherForPartition( + GaiaAuthConsumer* consumer) { + return signin_client_->CreateGaiaAuthFetcher(consumer, + gaia::GaiaSource::kChrome); +} + +network::mojom::CookieManager* +GaiaCookieManagerService::GetCookieManagerForPartition() { + return signin_client_->GetCookieManager(); +} + void GaiaCookieManagerService::InitializeListedAccountsIds() { for (gaia::ListedAccount& account : listed_accounts_) { DCHECK(account.id.empty()); @@ -1006,8 +1041,8 @@ void GaiaCookieManagerService::StartSetAccounts() { } oauth_multilogin_helper_ = std::make_unique<signin::OAuthMultiloginHelper>( - signin_client_, token_service_, requests_.front().GetMultiloginMode(), - requests_.front().GetAccounts(), + signin_client_, this, token_service_, + requests_.front().GetMultiloginMode(), requests_.front().GetAccounts(), external_cc_result_fetcher_.GetExternalCcResult(), base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished, weak_ptr_factory_.GetWeakPtr())); diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h index ca91666a74e..64348eb13a0 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h @@ -21,6 +21,7 @@ #include "components/prefs/pref_registry_simple.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/public/base/signin_client.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.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" @@ -54,8 +55,10 @@ enum class SetAccountsInCookieResult; // Also checks the External CC result to ensure no services that consume the // 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 { +class GaiaCookieManagerService + : public GaiaAuthConsumer, + public signin::AccountsCookieMutator::PartitionDelegate, + public network::mojom::CookieChangeListener { public: using AccountIdGaiaIdPair = std::pair<CoreAccountId, std::string>; @@ -71,6 +74,8 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, typedef base::OnceCallback<void(const CoreAccountId&, const GoogleServiceAuthError&)> AddAccountToCookieCompletedCallback; + typedef base::OnceCallback<void(const GoogleServiceAuthError&)> + LogOutFromCookieCompletedCallback; typedef base::RepeatingCallback<void(const std::vector<gaia::ListedAccount>&, const std::vector<gaia::ListedAccount>&, @@ -103,12 +108,16 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void RunAddAccountToCookieCompletedCallback( const CoreAccountId& account_id, const GoogleServiceAuthError& error); + void RunLogOutFromCookieCompletedCallback( + const GoogleServiceAuthError& error); static GaiaCookieRequest CreateAddAccountRequest( const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback callback); - static GaiaCookieRequest CreateLogOutRequest(gaia::GaiaSource source); + static GaiaCookieRequest CreateLogOutRequest( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback callback); static GaiaCookieRequest CreateListAccountsRequest(); static GaiaCookieRequest CreateSetAccountsRequest( gaia::MultiloginMode mode, @@ -142,6 +151,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, set_accounts_in_cookie_completed_callback_; AddAccountToCookieCompletedCallback add_account_to_cookie_completed_callback_; + LogOutFromCookieCompletedCallback log_out_from_cookie_completed_callback_; DISALLOW_COPY_AND_ASSIGN(GaiaCookieRequest); }; @@ -256,7 +266,8 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void CancelAll(); // Signout all accounts. - void LogOutAllAccounts(gaia::GaiaSource source); + void LogOutAllAccounts(gaia::GaiaSource source, + LogOutFromCookieCompletedCallback callback); // Call observers when setting accounts in cookie completes. void SignalSetAccountsComplete(signin::SetAccountsInCookieResult result); @@ -313,6 +324,9 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, const base::circular_deque<GaiaCookieRequest>::iterator& request, const GoogleServiceAuthError& error); + // Calls the LogOutFromCookie completion callback. + void SignalLogOutComplete(const GoogleServiceAuthError& error); + // Marks the list account being staled, and for iOS only, it triggers to fetch // the list of accounts (on iOS there is no OnCookieChange() notification). void MarkListAccountsStale(); @@ -331,6 +345,11 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void OnLogOutSuccess() override; void OnLogOutFailure(const GoogleServiceAuthError& error) override; + // Overridden from signin::AccountsCookieMutator::PartitionDelegate. + std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition( + GaiaAuthConsumer* consumer) override; + network::mojom::CookieManager* GetCookieManagerForPartition() override; + // Helper method to initialize listed accounts ids. void InitializeListedAccountsIds(); diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc index ede9d344628..369811b3473 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc @@ -10,6 +10,7 @@ #include <vector> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -41,6 +42,8 @@ const char kAccountId4[] = "account_id4"; using MockAddAccountToCookieCompletedCallback = base::MockCallback< GaiaCookieManagerService::AddAccountToCookieCompletedCallback>; +using MockLogOutFromCookieCompletedCallback = base::MockCallback< + GaiaCookieManagerService::LogOutFromCookieCompletedCallback>; class MockObserver { public: @@ -454,7 +457,11 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsNoQueue) { add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); + + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateLogOutSuccess(&helper); ASSERT_FALSE(helper.is_running()); } @@ -473,7 +480,10 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsFails) { add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + // A completion callback shouldn't be called. + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateLogOutFailure(&helper, error()); // CookieManagerService is still running; it is retrying the failed logout. ASSERT_TRUE(helper.is_running()); @@ -488,10 +498,13 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterOneAddInQueue) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run(account_id2_, no_error())); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); @@ -508,13 +521,16 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterTwoAddsInQueue) { add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, no_error())); EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, canceled())); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); // The Log Out should prevent this AddAccount from being fetched. helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); @@ -534,9 +550,16 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsTwice) { add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed1, + log_out_from_cookie_completed2; + EXPECT_CALL(log_out_from_cookie_completed1, Run(no_error())); + EXPECT_CALL(log_out_from_cookie_completed2, Run(canceled())); + + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed1.Get()); // Only one LogOut will be fetched. - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed2.Get()); SimulateLogOutSuccess(&helper); } @@ -556,7 +579,11 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeAdd) { add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); + + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); @@ -581,9 +608,16 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeLogoutAndAdd) { add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed1, + log_out_from_cookie_completed2; + EXPECT_CALL(log_out_from_cookie_completed1, Run(no_error())); + EXPECT_CALL(log_out_from_cookie_completed2, Run(canceled())); + + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed1.Get()); // Second LogOut will never be fetched. - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed2.Get()); helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); @@ -605,13 +639,16 @@ TEST_F(GaiaCookieManagerServiceTest, PendingSigninThenSignout) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed3; EXPECT_CALL(add_account_to_cookie_completed3, Run(account_id3_, no_error())); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); // Total sign in 2 times, not enforcing ordered sequences. EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); @@ -632,11 +669,15 @@ TEST_F(GaiaCookieManagerServiceTest, CancelSignIn) { EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, canceled())); EXPECT_CALL(helper, StartFetchingLogOut()); + MockLogOutFromCookieCompletedCallback log_out_from_cookie_completed; + EXPECT_CALL(log_out_from_cookie_completed, Run(no_error())); + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); - helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); + helper.LogOutAllAccounts(gaia::GaiaSource::kChrome, + log_out_from_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); diff --git a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc index 01c117c1a9d..00471573a28 100644 --- a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc @@ -10,6 +10,7 @@ #include <vector> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" @@ -124,9 +125,8 @@ class MutableProfileOAuth2TokenServiceDelegateTest web_database->LoadDatabase(); token_web_data_ = new TokenWebData(web_database, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get(), - WebDataServiceBase::ProfileErrorCallback()); - token_web_data_->Init(); + base::ThreadTaskRunnerHandle::Get()); + token_web_data_->Init(base::NullCallback()); } void AddSuccessfulOAuhTokenResponse() { diff --git a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.cc index 068076f824b..08d22b236cd 100644 --- a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.cc @@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -20,6 +21,8 @@ #include "mojo/public/cpp/bindings/callback_helpers.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +namespace signin { + namespace { constexpr int kMaxFetcherRetries = 3; @@ -36,7 +39,7 @@ std::string FindTokenForAccount( } CoreAccountId FindAccountIdForGaiaId( - const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts, + const std::vector<OAuthMultiloginHelper::AccountIdGaiaIdPair>& accounts, const std::string& gaia_id) { for (const auto& account : accounts) { if (gaia_id == account.second) @@ -47,30 +50,31 @@ CoreAccountId FindAccountIdForGaiaId( } // namespace -namespace signin { - OAuthMultiloginHelper::OAuthMultiloginHelper( SigninClient* signin_client, + AccountsCookieMutator::PartitionDelegate* partition_delegate, ProfileOAuth2TokenService* token_service, gaia::MultiloginMode mode, - const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts, + const std::vector<AccountIdGaiaIdPair>& accounts, const std::string& external_cc_result, base::OnceCallback<void(SetAccountsInCookieResult)> callback) : signin_client_(signin_client), + partition_delegate_(partition_delegate), token_service_(token_service), mode_(mode), accounts_(accounts), external_cc_result_(external_cc_result), callback_(std::move(callback)) { DCHECK(signin_client_); + DCHECK(partition_delegate_); DCHECK(token_service_); DCHECK(!accounts_.empty()); DCHECK(callback_); #ifndef NDEBUG // Check that there is no duplicate accounts. - std::set<GaiaCookieManagerService::AccountIdGaiaIdPair> - accounts_no_duplicates(accounts_.begin(), accounts_.end()); + std::set<AccountIdGaiaIdPair> accounts_no_duplicates(accounts_.begin(), + accounts_.end()); DCHECK_EQ(accounts_.size(), accounts_no_duplicates.size()); #endif @@ -125,7 +129,7 @@ void OAuthMultiloginHelper::OnAccessTokensFailure( void OAuthMultiloginHelper::StartFetchingMultiLogin() { DCHECK_EQ(gaia_id_token_pairs_.size(), accounts_.size()); gaia_auth_fetcher_ = - signin_client_->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome); + partition_delegate_->CreateGaiaAuthFetcherForPartition(this); gaia_auth_fetcher_->StartOAuthMultilogin(mode_, gaia_id_token_pairs_, external_cc_result_); } @@ -179,7 +183,7 @@ void OAuthMultiloginHelper::StartSettingCookies( const OAuthMultiloginResult& result) { DCHECK(cookies_to_set_.empty()); network::mojom::CookieManager* cookie_manager = - signin_client_->GetCookieManager(); + partition_delegate_->GetCookieManagerForPartition(); const std::vector<net::CanonicalCookie>& cookies = result.cookies(); for (const net::CanonicalCookie& cookie : cookies) { @@ -196,7 +200,7 @@ void OAuthMultiloginHelper::StartSettingCookies( 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); + net::CookieOptions::SameSiteCookieContext::MakeInclusive()); cookie_manager->SetCanonicalCookie( cookie, "https", options, mojo::WrapCallbackWithDefaultInvokeIfNotRun( diff --git a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h index 3e88ae58a2e..ab3e5d9be73 100644 --- a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h @@ -13,8 +13,8 @@ #include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" #include "components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" @@ -23,7 +23,6 @@ class GaiaAuthFetcher; class GoogleServiceAuthError; class ProfileOAuth2TokenService; -class SigninClient; namespace signin { @@ -37,12 +36,14 @@ enum class SetAccountsInCookieResult; // It is safe to delete this object from within the callbacks. class OAuthMultiloginHelper : public GaiaAuthConsumer { public: + using AccountIdGaiaIdPair = std::pair<CoreAccountId, std::string>; + OAuthMultiloginHelper( SigninClient* signin_client, + AccountsCookieMutator::PartitionDelegate* partition_delegate, ProfileOAuth2TokenService* token_service, gaia::MultiloginMode mode, - const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& - accounts, + const std::vector<AccountIdGaiaIdPair>& accounts, const std::string& external_cc_result, base::OnceCallback<void(SetAccountsInCookieResult)> callback); @@ -73,13 +74,14 @@ class OAuthMultiloginHelper : public GaiaAuthConsumer { net::CanonicalCookie::CookieInclusionStatus status); SigninClient* signin_client_; + AccountsCookieMutator::PartitionDelegate* partition_delegate_; ProfileOAuth2TokenService* token_service_; int fetcher_retries_ = 0; gaia::MultiloginMode mode_; // Account ids to set in the cookie. - const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> accounts_; + const std::vector<AccountIdGaiaIdPair> accounts_; // See GaiaCookieManagerService::ExternalCcResultFetcher for details. const std::string external_cc_result_; // Access tokens, in the same order as the account ids. diff --git a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc index 70fe65f08da..3df6e758629 100644 --- a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc @@ -11,6 +11,7 @@ #include "components/prefs/testing_pref_service.h" #include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" #include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "google_apis/gaia/gaia_urls.h" #include "services/network/test/test_cookie_manager.h" @@ -162,24 +163,21 @@ class MockTokenService : public FakeProfileOAuth2TokenService { } // namespace -class OAuthMultiloginHelperTest : public testing::Test { +class OAuthMultiloginHelperTest + : public testing::Test, + public AccountsCookieMutator::PartitionDelegate { public: OAuthMultiloginHelperTest() : test_signin_client_(&pref_service_), mock_token_service_(&pref_service_) { - 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<GaiaCookieManagerService::AccountIdGaiaIdPair> - accounts) { + const std::vector<OAuthMultiloginHelper::AccountIdGaiaIdPair> accounts) { return std::make_unique<OAuthMultiloginHelper>( - &test_signin_client_, token_service(), + &test_signin_client_, this, token_service(), gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts, std::string(), base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, @@ -187,10 +185,9 @@ class OAuthMultiloginHelperTest : public testing::Test { } std::unique_ptr<OAuthMultiloginHelper> CreateHelperWithExternalCcResult( - const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> - accounts) { + const std::vector<OAuthMultiloginHelper::AccountIdGaiaIdPair> accounts) { return std::make_unique<OAuthMultiloginHelper>( - &test_signin_client_, token_service(), + &test_signin_client_, this, token_service(), gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts, kExternalCcResult, base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, @@ -212,7 +209,7 @@ class OAuthMultiloginHelperTest : public testing::Test { kExternalCcResult; } - MockCookieManager* cookie_manager() { return mock_cookie_manager_; } + MockCookieManager* cookie_manager() { return &mock_cookie_manager_; } MockTokenService* token_service() { return &mock_token_service_; } protected: @@ -222,13 +219,24 @@ class OAuthMultiloginHelperTest : public testing::Test { result_ = result; } + // AccountsCookieMuator::PartitionDelegate: + std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition( + GaiaAuthConsumer* consumer) override { + return test_signin_client_.CreateGaiaAuthFetcher(consumer, + gaia::GaiaSource::kChrome); + } + + network::mojom::CookieManager* GetCookieManagerForPartition() override { + return &mock_cookie_manager_; + } + base::test::TaskEnvironment task_environment_; bool callback_called_ = false; SetAccountsInCookieResult result_; TestingPrefServiceSimple pref_service_; - MockCookieManager* mock_cookie_manager_; // Owned by test_signin_client_ + MockCookieManager mock_cookie_manager_; TestSigninClient test_signin_client_; MockTokenService mock_token_service_; }; diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc index aebcdb1d75a..3c80d8d09c4 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc @@ -289,18 +289,32 @@ void PrimaryAccountManager::SignOutAndKeepAllAccounts( StartSignOut(signout_source_metric, signout_delete_metric, RemoveAccountsOption::kKeepAllAccounts); } +#endif // !defined(OS_CHROMEOS) + +#if defined(OS_CHROMEOS) +void PrimaryAccountManager::RevokeSyncConsent() { + DCHECK(IsAuthenticated()); + // TODO(https://crbug.com/1046746): Don't record metrics here. + StartSignOut(signin_metrics::ProfileSignout::USER_CLICKED_SIGNOUT_SETTINGS, + signin_metrics::SignoutDelete::KEEPING, + RemoveAccountsOption::kKeepAllAccounts, + /*assert_signout_allowed=*/true); +} +#endif // defined(OS_CHROMEOS) void PrimaryAccountManager::StartSignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option) { + RemoveAccountsOption remove_option, + bool assert_signout_allowed) { VLOG(1) << "StartSignOut: " << static_cast<int>(signout_source_metric) << ", " << static_cast<int>(signout_delete_metric) << ", " << static_cast<int>(remove_option); client_->PreSignOut( base::BindOnce(&PrimaryAccountManager::OnSignoutDecisionReached, base::Unretained(this), signout_source_metric, - signout_delete_metric, remove_option), + signout_delete_metric, remove_option, + assert_signout_allowed), signout_source_metric); } @@ -308,8 +322,11 @@ void PrimaryAccountManager::OnSignoutDecisionReached( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, RemoveAccountsOption remove_option, + bool assert_signout_allowed, SigninClient::SignoutDecision signout_decision) { DCHECK(IsInitialized()); + if (assert_signout_allowed) + DCHECK_EQ(SigninClient::SignoutDecision::ALLOW_SIGNOUT, signout_decision); VLOG(1) << "OnSignoutDecisionReached: " << (signout_decision == SigninClient::SignoutDecision::ALLOW_SIGNOUT); @@ -333,6 +350,7 @@ void PrimaryAccountManager::OnSignoutDecisionReached( // may be components that don't listen for token service events when the // profile is not connected to an account. switch (remove_option) { +#if !defined(OS_CHROMEOS) case RemoveAccountsOption::kRemoveAllAccounts: VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out"; token_service_->RevokeAllCredentials( @@ -346,6 +364,7 @@ void PrimaryAccountManager::OnSignoutDecisionReached( signin_metrics::SourceForRefreshTokenOperation:: kPrimaryAccountManager_ClearAccount); break; +#endif case RemoveAccountsOption::kKeepAllAccounts: // Do nothing. break; @@ -355,6 +374,7 @@ void PrimaryAccountManager::OnSignoutDecisionReached( observer.GoogleSignedOut(account_info); } +#if !defined(OS_CHROMEOS) void PrimaryAccountManager::OnRefreshTokensLoaded() { token_service_->RemoveObserver(this); diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager.h b/chromium/components/signin/internal/identity_manager/primary_account_manager.h index 6945d780379..b00f9381a92 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_manager.h +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.h @@ -54,23 +54,21 @@ class PrimaryAccountManager : public ProfileOAuth2TokenServiceObserver { virtual void UnconsentedPrimaryAccountChanged(const CoreAccountInfo& info) { } -#if !defined(OS_CHROMEOS) // Called whenever the currently signed-in user has been signed out. virtual void GoogleSignedOut(const CoreAccountInfo& info) {} -#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 = 0, +#if !defined(OS_CHROMEOS) // Remove all the accounts. kRemoveAllAccounts, // Removes the authenticated account if it is in authentication error. kRemoveAuthenticatedAccountIfInError - }; #endif + }; PrimaryAccountManager( SigninClient* client, @@ -142,7 +140,14 @@ class PrimaryAccountManager : public ProfileOAuth2TokenServiceObserver { void SignOutAndKeepAllAccounts( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric); -#endif +#endif // !defined(OS_CHROMEOS) + +#if defined(OS_CHROMEOS) + // Revokes sync consent from the primary account. The primary account must + // have sync consent. After the call a primary account will remain but it will + // not have sync consent. + void RevokeSyncConsent(); +#endif // defined(OS_CHROMEOS) // Adds and removes observers. void AddObserver(Observer* observer); @@ -175,19 +180,22 @@ class PrimaryAccountManager : public ProfileOAuth2TokenServiceObserver { void SetPrimaryAccountInternal(const CoreAccountInfo& account_info, bool consented_to_sync); -#if !defined(OS_CHROMEOS) - // Starts the sign out process. + // Starts the sign out process. If |assert_signout_allowed| is true then + // the sign out process will DCHECK if user sign out is not allowed. void StartSignOut(signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option); + RemoveAccountsOption remove_option, + bool assert_signout_allowed = false); // 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, + bool assert_signout_allowed, SigninClient::SignoutDecision signout_decision); +#if !defined(OS_CHROMEOS) // ProfileOAuth2TokenServiceObserver: void OnRefreshTokensLoaded() override; #endif diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc index 62544a7688e..e00eb5e070e 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc @@ -480,3 +480,18 @@ TEST_F(PrimaryAccountManagerTest, SetUnconsentedPrimaryAccountInfo) { EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo()); EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo()); } + +#if defined(OS_CHROMEOS) +TEST_F(PrimaryAccountManagerTest, RevokeSyncConsent) { + CreatePrimaryAccountManager(); + CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com"); + manager_->SignIn("user@gmail.com"); + EXPECT_TRUE(manager_->IsAuthenticated()); + + manager_->RevokeSyncConsent(); + EXPECT_FALSE(manager_->IsAuthenticated()); + EXPECT_TRUE(manager_->HasUnconsentedPrimaryAccount()); + EXPECT_EQ(account_id, + manager_->GetUnconsentedPrimaryAccountInfo().account_id); +} +#endif // defined(OS_CHROMEOS) diff --git a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc index 85de348118b..668f660289a 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc @@ -52,6 +52,19 @@ bool PrimaryAccountMutatorImpl::SetPrimaryAccount( } #if defined(OS_CHROMEOS) +void PrimaryAccountMutatorImpl::RevokeSyncConsent() { + primary_account_manager_->RevokeSyncConsent(); +} + +void PrimaryAccountMutatorImpl::SetUnconsentedPrimaryAccount( + const CoreAccountId& account_id) { + // On Chrome OS the UPA can only be set once and never removed or changed. + DCHECK(!account_id.empty()); + DCHECK(!primary_account_manager_->HasUnconsentedPrimaryAccount()); + AccountInfo account_info = account_tracker_->GetAccountInfo(account_id); + primary_account_manager_->SetUnconsentedPrimaryAccountInfo(account_info); +} + bool PrimaryAccountMutatorImpl::DeprecatedSetPrimaryAccountAndUpdateAccountInfo( const std::string& gaia_id, const std::string& email) { diff --git a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h index 91806ee41ca..184c3ac4a5b 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h @@ -27,6 +27,8 @@ class PrimaryAccountMutatorImpl : public PrimaryAccountMutator { // PrimaryAccountMutator implementation. bool SetPrimaryAccount(const CoreAccountId& account_id) override; #if defined(OS_CHROMEOS) + void RevokeSyncConsent() override; + void SetUnconsentedPrimaryAccount(const CoreAccountId& account_id) override; bool DeprecatedSetPrimaryAccountAndUpdateAccountInfo( const std::string& gaia_id, const std::string& email) override; diff --git a/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc index f87e3b90655..bf35da6fc54 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc @@ -29,15 +29,16 @@ void PrimaryAccountPolicyManagerImpl::InitializePolicy( local_state_pref_registrar_.Init(local_state); local_state_pref_registrar_.Add( prefs::kGoogleServicesUsernamePattern, - base::Bind(&PrimaryAccountPolicyManagerImpl:: - OnGoogleServicesUsernamePatternChanged, - weak_pointer_factory_.GetWeakPtr(), - primary_account_manager)); + base::BindRepeating(&PrimaryAccountPolicyManagerImpl:: + OnGoogleServicesUsernamePatternChanged, + weak_pointer_factory_.GetWeakPtr(), + primary_account_manager)); } signin_allowed_.Init( prefs::kSigninAllowed, client_->GetPrefs(), - base::Bind(&PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged, - base::Unretained(this), primary_account_manager)); + base::BindRepeating( + &PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged, + base::Unretained(this), primary_account_manager)); CoreAccountInfo account_info = primary_account_manager->GetAuthenticatedAccountInfo(); diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc index cb0cbf83191..f5dc498ddd6 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc @@ -17,7 +17,7 @@ #if defined(OS_ANDROID) #include "components/signin/internal/base/account_manager_facade_android.h" -#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h" #else #include "components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h" #include "components/signin/public/webdata/token_web_data.h" @@ -39,14 +39,14 @@ namespace { #if defined(OS_ANDROID) // TODO(crbug.com/986435) Provide AccountManagerFacade as a parameter once // IdentityServicesProvider owns its instance management. -std::unique_ptr<OAuth2TokenServiceDelegateAndroid> CreateAndroidOAuthDelegate( - AccountTrackerService* account_tracker_service) { +std::unique_ptr<ProfileOAuth2TokenServiceDelegateAndroid> +CreateAndroidOAuthDelegate(AccountTrackerService* account_tracker_service) { auto account_manager_facade = - OAuth2TokenServiceDelegateAndroid:: + ProfileOAuth2TokenServiceDelegateAndroid:: get_disable_interaction_with_system_accounts() ? nullptr : AccountManagerFacadeAndroid::GetJavaObject(); - return std::make_unique<OAuth2TokenServiceDelegateAndroid>( + return std::make_unique<ProfileOAuth2TokenServiceDelegateAndroid>( account_tracker_service, account_manager_facade); } #elif defined(OS_IOS) diff --git a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc index 9b2fac4bf35..4e8dff2c4e2 100644 --- a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h" #include "base/android/jni_android.h" #include "base/android/jni_array.h" @@ -14,7 +14,7 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" -#include "components/signin/internal/identity_manager/android/jni_headers/OAuth2TokenService_jni.h" +#include "components/signin/public/android/jni_headers/ProfileOAuth2TokenServiceDelegate_jni.h" #include "components/signin/public/base/account_consistency_method.h" #include "components/signin/public/identity_manager/account_info.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -35,14 +35,14 @@ namespace { // - the OAuth2 access token. // - the expiry time of the token (may be null, indicating that the expiry // time is unknown. -typedef base::Callback< +typedef base::OnceCallback< void(const GoogleServiceAuthError&, const std::string&, const base::Time&)> FetchOAuth2TokenCallback; class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher { public: AndroidAccessTokenFetcher( - OAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate, + ProfileOAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate, OAuth2AccessTokenConsumer* consumer, const std::string& account_id); ~AndroidAccessTokenFetcher() override; @@ -61,7 +61,7 @@ class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher { private: std::string CombineScopes(const std::vector<std::string>& scopes); - OAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate_; + ProfileOAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate_; std::string account_id_; bool request_was_cancelled_; base::WeakPtrFactory<AndroidAccessTokenFetcher> weak_factory_; @@ -70,7 +70,7 @@ class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher { }; AndroidAccessTokenFetcher::AndroidAccessTokenFetcher( - OAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate, + ProfileOAuth2TokenServiceDelegateAndroid* oauth2_token_service_delegate, OAuth2AccessTokenConsumer* consumer, const std::string& account_id) : OAuth2AccessTokenFetcher(consumer), @@ -91,11 +91,11 @@ void AndroidAccessTokenFetcher::Start(const std::string& client_id, ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope); std::unique_ptr<FetchOAuth2TokenCallback> heap_callback( new FetchOAuth2TokenCallback( - base::Bind(&AndroidAccessTokenFetcher::OnAccessTokenResponse, - weak_factory_.GetWeakPtr()))); + base::BindOnce(&AndroidAccessTokenFetcher::OnAccessTokenResponse, + weak_factory_.GetWeakPtr()))); // Call into Java to get a new token. - signin::Java_OAuth2TokenService_getAccessTokenFromNative( + signin::Java_ProfileOAuth2TokenServiceDelegate_getAccessTokenFromNative( env, oauth2_token_service_delegate_->GetJavaObject(), j_username, j_scope, reinterpret_cast<intptr_t>(heap_callback.release())); } @@ -138,21 +138,22 @@ std::string AndroidAccessTokenFetcher::CombineScopes( } // namespace // TODO(crbug.com/1009957) Remove disable_interation_with_system_accounts_ -// from OAuth2TokenServiceDelegateAndroid -bool OAuth2TokenServiceDelegateAndroid:: +// from ProfileOAuth2TokenServiceDelegateAndroid +bool ProfileOAuth2TokenServiceDelegateAndroid:: disable_interaction_with_system_accounts_ = false; -OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid( - AccountTrackerService* account_tracker_service, - const base::android::JavaRef<jobject>& account_manager_facade) +ProfileOAuth2TokenServiceDelegateAndroid:: + ProfileOAuth2TokenServiceDelegateAndroid( + AccountTrackerService* account_tracker_service, + const base::android::JavaRef<jobject>& account_manager_facade) : account_tracker_service_(account_tracker_service), fire_refresh_token_loaded_(RT_LOAD_NOT_START) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ctor"; + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::ctor"; DCHECK(account_tracker_service_); JNIEnv* env = AttachCurrentThread(); base::android::ScopedJavaLocalRef<jobject> local_java_ref = - signin::Java_OAuth2TokenService_create( + signin::Java_ProfileOAuth2TokenServiceDelegate_create( env, reinterpret_cast<intptr_t>(this), account_tracker_service_->GetJavaObject(), account_manager_facade); java_ref_.Reset(env, local_java_ref.obj()); @@ -172,48 +173,52 @@ OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid( } } -OAuth2TokenServiceDelegateAndroid::~OAuth2TokenServiceDelegateAndroid() {} +ProfileOAuth2TokenServiceDelegateAndroid:: + ~ProfileOAuth2TokenServiceDelegateAndroid() {} -ScopedJavaLocalRef<jobject> OAuth2TokenServiceDelegateAndroid::GetJavaObject() { +ScopedJavaLocalRef<jobject> +ProfileOAuth2TokenServiceDelegateAndroid::GetJavaObject() { return ScopedJavaLocalRef<jobject>(java_ref_); } -bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable( +bool ProfileOAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable( const CoreAccountId& account_id) const { DCHECK(!disable_interaction_with_system_accounts_) << __FUNCTION__ << " needs to interact with system accounts and cannot be used with " "disable_interaction_with_system_accounts_"; - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" - << " account= " << account_id; + DVLOG(1) + << "ProfileOAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" + << " account= " << account_id; std::string account_name = MapAccountIdToAccountName(account_id); if (account_name.empty()) { // This corresponds to the case when the account with id |account_id| is not // present on the device and thus was not seeded. - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" - << " cannot find account name for account id " << account_id; + DVLOG(1) + << "ProfileOAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" + << " cannot find account name for account id " << account_id; return false; } JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_account_id = ConvertUTF8ToJavaString(env, account_name); jboolean refresh_token_is_available = - signin::Java_OAuth2TokenService_hasOAuth2RefreshToken(env, java_ref_, - j_account_id); + signin::Java_ProfileOAuth2TokenServiceDelegate_hasOAuth2RefreshToken( + env, java_ref_, j_account_id); return refresh_token_is_available == JNI_TRUE; } -GoogleServiceAuthError OAuth2TokenServiceDelegateAndroid::GetAuthError( +GoogleServiceAuthError ProfileOAuth2TokenServiceDelegateAndroid::GetAuthError( const CoreAccountId& account_id) const { auto it = errors_.find(account_id); return (it == errors_.end()) ? GoogleServiceAuthError::AuthErrorNone() : it->second; } -void OAuth2TokenServiceDelegateAndroid::UpdateAuthError( +void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAuthError( const CoreAccountId& account_id, const GoogleServiceAuthError& error) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAuthError" << " account=" << account_id << " error=" << error.ToString(); if (error.IsTransientError()) @@ -232,29 +237,13 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAuthError( FireAuthErrorChanged(account_id, error); } -std::vector<CoreAccountId> OAuth2TokenServiceDelegateAndroid::GetAccounts() - const { - std::vector<std::string> accounts; - JNIEnv* env = AttachCurrentThread(); - - // TODO(crbug.com/1028580) Pass |j_accounts| as a list of - // org.chromium.components.signin.identitymanager.CoreAccountId and convert it - // to CoreAccountId using ConvertFromJavaCoreAccountId. - ScopedJavaLocalRef<jobjectArray> j_accounts = - signin::Java_OAuth2TokenService_getAccounts(env); - - // TODO(fgorski): We may decide to filter out some of the accounts. - base::android::AppendJavaStringArrayToStringVector(env, j_accounts, - &accounts); - std::vector<CoreAccountId> account_ids; - for (auto& account : accounts) - account_ids.push_back(CoreAccountId::FromString(account)); - - return account_ids; +std::vector<CoreAccountId> +ProfileOAuth2TokenServiceDelegateAndroid::GetAccounts() const { + return accounts_; } std::vector<std::string> -OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { +ProfileOAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { DCHECK(!disable_interaction_with_system_accounts_) << __FUNCTION__ << " needs to interact with system accounts and cannot be used with " @@ -266,14 +255,15 @@ OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { // org.chromium.components.signin.identitymanager.CoreAccountId and convert it // to CoreAccountId using ConvertFromJavaCoreAccountId. ScopedJavaLocalRef<jobjectArray> j_accounts = - signin::Java_OAuth2TokenService_getSystemAccountNames(env, java_ref_); + signin::Java_ProfileOAuth2TokenServiceDelegate_getSystemAccountNames( + env, java_ref_); base::android::AppendJavaStringArrayToStringVector(env, j_accounts, &account_names); return account_names; } std::vector<CoreAccountId> -OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { +ProfileOAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { std::vector<CoreAccountId> ids; for (const std::string& name : GetSystemAccountNames()) { CoreAccountId id(MapAccountNameToAccountId(name)); @@ -284,7 +274,7 @@ OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { } std::vector<CoreAccountId> -OAuth2TokenServiceDelegateAndroid::GetValidAccounts() { +ProfileOAuth2TokenServiceDelegateAndroid::GetValidAccounts() { std::vector<CoreAccountId> ids; for (const CoreAccountId& id : GetAccounts()) { if (ValidateAccountId(id)) @@ -293,21 +283,19 @@ OAuth2TokenServiceDelegateAndroid::GetValidAccounts() { return ids; } -void OAuth2TokenServiceDelegateAndroid::SetAccounts( +void ProfileOAuth2TokenServiceDelegateAndroid::SetAccounts( const std::vector<CoreAccountId>& accounts) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobjectArray> java_accounts( - base::android::ToJavaArrayOfStrings(env, ToStringList(accounts))); - signin::Java_OAuth2TokenService_setAccounts(env, java_accounts); + accounts_ = accounts; } std::unique_ptr<OAuth2AccessTokenFetcher> -OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( +ProfileOAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_factory, OAuth2AccessTokenConsumer* consumer) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher" - << " account= " << account_id; + DVLOG(1) + << "ProfileOAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher" + << " account= " << account_id; ValidateAccountId(account_id); std::string account_name = MapAccountIdToAccountName(account_id); DCHECK(!account_name.empty()) @@ -316,7 +304,7 @@ OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( account_name); } -void OAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated( +void ProfileOAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated( const CoreAccountId& account_id, const std::string& client_id, const OAuth2AccessTokenManager::ScopeSet& scopes, @@ -329,11 +317,11 @@ void OAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated( JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_access_token = ConvertUTF8ToJavaString(env, access_token); - signin::Java_OAuth2TokenService_invalidateAccessToken(env, java_ref_, - j_access_token); + signin::Java_ProfileOAuth2TokenServiceDelegate_invalidateAccessToken( + env, java_ref_, j_access_token); } -void OAuth2TokenServiceDelegateAndroid:: +void ProfileOAuth2TokenServiceDelegateAndroid:: ReloadAllAccountsFromSystemWithPrimaryAccount( const base::Optional<CoreAccountId>& primary_account_id) { JNIEnv* env = AttachCurrentThread(); @@ -342,14 +330,12 @@ void OAuth2TokenServiceDelegateAndroid:: primary_account_id.has_value() ? ConvertUTF8ToJavaString(env, primary_account_id->ToString()) : nullptr; - signin::Java_OAuth2TokenService_seedAndReloadAccountsWithPrimaryAccount( - env, java_ref_, j_account_id); + signin:: + Java_ProfileOAuth2TokenServiceDelegate_seedAndReloadAccountsWithPrimaryAccount( + env, java_ref_, j_account_id); } -// TODO(crbug.com/1028580) Pass |account_id| as a -// org.chromium.components.signin.identitymanager.CoreAccountId and convert it -// to CoreAccountId using ConvertFromJavaCoreAccountId. -void OAuth2TokenServiceDelegateAndroid:: +void ProfileOAuth2TokenServiceDelegateAndroid:: ReloadAllAccountsWithPrimaryAccountAfterSeeding( JNIEnv* env, const base::android::JavaParamRef<jstring>& account_id) { @@ -361,11 +347,11 @@ void OAuth2TokenServiceDelegateAndroid:: UpdateAccountList(core_account_id, GetValidAccounts(), GetSystemAccounts()); } -void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( +void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList( const base::Optional<CoreAccountId>& signed_in_account_id, const std::vector<CoreAccountId>& prev_ids, const std::vector<CoreAccountId>& curr_ids) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << " sigined_in_account_id=" << (signed_in_account_id.has_value() ? signed_in_account_id->ToString() @@ -412,59 +398,53 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( !signed_in_account_id.has_value()) { account_tracker_service_->SetMigrationDone(); } - - if (!last_update_accounts_time_.is_null()) { - base::TimeDelta sample = base::Time::Now() - last_update_accounts_time_; - UmaHistogramLongTimes("Signin.AndroidTimeBetweenUpdateAccountList", sample); - } - last_update_accounts_time_ = base::Time::Now(); } -bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( +bool ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList( const base::Optional<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) || - (signed_in_id.has_value() && base::Contains(curr_ids, *signed_in_id)); + signed_in_id.has_value() && base::Contains(curr_ids, *signed_in_id); if (keep_accounts) { // Revoke token for ids that have been removed from the device. for (const CoreAccountId& prev_id : prev_ids) { if (signed_in_id.has_value() && prev_id == *signed_in_id) continue; if (!base::Contains(curr_ids, prev_id)) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" - << "revoked=" << prev_id; + DVLOG(1) + << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + << "revoked=" << prev_id; revoked_ids->push_back(prev_id); } } if (signed_in_id.has_value()) { // Always fire the primary signed in account first. - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "refreshed=" << *signed_in_id; refreshed_ids->push_back(*signed_in_id); } for (const CoreAccountId& curr_id : curr_ids) { if (signed_in_id.has_value() && curr_id == *signed_in_id) continue; - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "refreshed=" << curr_id; refreshed_ids->push_back(curr_id); } } else { // Revoke all ids with signed in account first. if (signed_in_id.has_value() && base::Contains(prev_ids, *signed_in_id)) { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "revoked=" << *signed_in_id; revoked_ids->push_back(*signed_in_id); } for (const CoreAccountId& prev_id : prev_ids) { if (signed_in_id.has_value() && prev_id == *signed_in_id) continue; - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "revoked=" << prev_id; revoked_ids->push_back(prev_id); } @@ -472,8 +452,9 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( return keep_accounts; } -void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded"; +void ProfileOAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() { + DVLOG(1) + << "ProfileOAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded"; DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, load_credentials_state()); set_load_credentials_state( @@ -481,8 +462,8 @@ void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() { ProfileOAuth2TokenServiceDelegate::FireRefreshTokensLoaded(); } -void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials"; +void ProfileOAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { + DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::RevokeAllCredentials"; ScopedBatchChange batch(this); std::vector<CoreAccountId> accounts_to_revoke = GetAccounts(); @@ -494,14 +475,13 @@ void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { FireRefreshTokenRevoked(account); } -void OAuth2TokenServiceDelegateAndroid::LoadCredentials( +void ProfileOAuth2TokenServiceDelegateAndroid::LoadCredentials( const CoreAccountId& primary_account_id) { DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, load_credentials_state()); set_load_credentials_state( signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS); - if (primary_account_id.empty() && - !base::FeatureList::IsEnabled(signin::kMiceFeature)) { + if (primary_account_id.empty()) { FireRefreshTokensLoaded(); return; } @@ -513,12 +493,13 @@ void OAuth2TokenServiceDelegateAndroid::LoadCredentials( } } -std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName( +std::string ProfileOAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName( const CoreAccountId& account_id) const { return account_tracker_service_->GetAccountInfo(account_id).email; } -CoreAccountId OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( +CoreAccountId +ProfileOAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( const std::string& account_name) const { CoreAccountId account_id = account_tracker_service_->FindAccountInfoByEmail(account_name).account_id; @@ -530,7 +511,7 @@ CoreAccountId OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( namespace signin { // Called from Java when fetching of an OAuth2 token is finished. The // |authToken| param is only valid when |result| is true. -void JNI_OAuth2TokenService_OnOAuth2TokenFetched( +void JNI_ProfileOAuth2TokenServiceDelegate_OnOAuth2TokenFetched( JNIEnv* env, const JavaParamRef<jstring>& authToken, jboolean isTransientError, @@ -549,6 +530,6 @@ void JNI_OAuth2TokenService_OnOAuth2TokenFetched( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_REJECTED_BY_SERVER); } - heap_callback->Run(err, token, base::Time()); + std::move(*heap_callback).Run(err, token, base::Time()); } } // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h index 7aa85189ec8..63c267a2b65 100644 --- a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ -#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ #include <map> #include <memory> @@ -14,7 +14,6 @@ #include "base/android/scoped_java_ref.h" #include "base/callback.h" #include "base/macros.h" -#include "base/time/time.h" #include "components/signin/internal/identity_manager/account_tracker_service.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" @@ -27,16 +26,16 @@ // See |ProfileOAuth2TokenServiceDelegate| for usage details. // // Note: requests should be started from the UI thread. -class OAuth2TokenServiceDelegateAndroid +class ProfileOAuth2TokenServiceDelegateAndroid : public ProfileOAuth2TokenServiceDelegate { public: - OAuth2TokenServiceDelegateAndroid( + ProfileOAuth2TokenServiceDelegateAndroid( AccountTrackerService* account_tracker_service, const base::android::JavaRef<jobject>& account_manager_facade); - ~OAuth2TokenServiceDelegateAndroid() override; + ~ProfileOAuth2TokenServiceDelegateAndroid() override; - // Creates a new instance of the OAuth2TokenServiceDelegateAndroid. - static OAuth2TokenServiceDelegateAndroid* Create(); + // Creates a new instance of the ProfileOAuth2TokenServiceDelegateAndroid. + static ProfileOAuth2TokenServiceDelegateAndroid* Create(); // Returns a reference to the corresponding Java OAuth2TokenService object. base::android::ScopedJavaLocalRef<jobject> GetJavaObject() override; @@ -45,7 +44,7 @@ class OAuth2TokenServiceDelegateAndroid // tests. This prevents the token service from building the java objects // which require prior initialization (AccountManagerFacade) // TODO(crbug.com/1009957) Remove disable_interation_with_system_accounts_ - // from OAuth2TokenServiceDelegateAndroid + // from ProfileOAuth2TokenServiceDelegateAndroid static void set_disable_interaction_with_system_accounts() { disable_interaction_with_system_accounts_ = true; } @@ -72,8 +71,8 @@ class OAuth2TokenServiceDelegateAndroid const base::Optional<CoreAccountId>& primary_account_id) override; // Resumes the reload of accounts once the account seeding is complete. - // TODO(crbug.com/934688) Once OAuth2TokenService.java is internalized, use - // CoreAccountId instead of String. + // TODO(crbug.com/934688) Once ProfileOAuth2TokenServiceDelegate.java is + // internalized, use CoreAccountId instead of String. void ReloadAllAccountsWithPrimaryAccountAfterSeeding( JNIEnv* env, const base::android::JavaParamRef<jstring>& account_id); @@ -130,25 +129,27 @@ class OAuth2TokenServiceDelegateAndroid std::vector<CoreAccountId> GetSystemAccounts(); // As |GetAccounts| but with only validated account IDs. std::vector<CoreAccountId> GetValidAccounts(); - // Set accounts using Java's Oauth2TokenService.setAccounts. + // Set accounts that have been advertised by OnRefreshTokenAvailable. virtual void SetAccounts(const std::vector<CoreAccountId>& accounts); base::android::ScopedJavaGlobalRef<jobject> java_ref_; + // Accounts that have been advertised by OnRefreshTokenAvailable. + std::vector<CoreAccountId> accounts_; + // Maps account_id to the last error for that account. std::map<CoreAccountId, GoogleServiceAuthError> errors_; AccountTrackerService* account_tracker_service_; RefreshTokenLoadStatus fire_refresh_token_loaded_; - base::Time last_update_accounts_time_; // For testing, disables the creation of the java counterpart, see // set_disable_interaction_with_system_accounts(). // TODO(crbug.com/1009957) Remove disable_interation_with_system_accounts_ - // from OAuth2TokenServiceDelegateAndroid + // from ProfileOAuth2TokenServiceDelegateAndroid static bool disable_interaction_with_system_accounts_; - DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceDelegateAndroid); + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceDelegateAndroid); }; -#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ diff --git a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android_unittest.cc index a9de2f5aaea..0ae09eb25b8 100644 --- a/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gmock/include/gmock/gmock.h" @@ -19,11 +19,12 @@ namespace signin { namespace { const std::vector<CoreAccountId> kEmptyVector; class OAuth2TokenServiceDelegateAndroidForTest - : public OAuth2TokenServiceDelegateAndroid { + : public ProfileOAuth2TokenServiceDelegateAndroid { public: OAuth2TokenServiceDelegateAndroidForTest( AccountTrackerService* account_tracker_service) - : OAuth2TokenServiceDelegateAndroid(account_tracker_service, nullptr) {} + : ProfileOAuth2TokenServiceDelegateAndroid(account_tracker_service, + nullptr) {} MOCK_METHOD1(SetAccounts, void(const std::vector<CoreAccountId>&)); }; diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm index d93f2428cf9..ea2e8027964 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm @@ -22,7 +22,7 @@ #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/ios/device_accounts_provider.h" #include "google_apis/gaia/oauth2_access_token_fetcher.h" -#include "net/url_request/url_request_status.h" +#include "net/base/net_errors.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -59,8 +59,7 @@ GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError( GoogleServiceAuthError::SERVICE_UNAVAILABLE); case kAuthenticationErrorCategoryNetworkServerErrors: // Just set the connection error state to FAILED. - return GoogleServiceAuthError::FromConnectionError( - net::URLRequestStatus::FAILED); + return GoogleServiceAuthError::FromConnectionError(net::ERR_FAILED); case kAuthenticationErrorCategoryUserCancellationErrors: return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); case kAuthenticationErrorCategoryUnknownIdentityErrors: diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm index b0eeef2cb63..cc1995dea29 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm @@ -18,7 +18,6 @@ #include "google_apis/gaia/oauth2_access_token_consumer.h" #include "google_apis/gaia/oauth2_access_token_fetcher.h" #include "google_apis/gaia/oauth2_access_token_manager_test_util.h" -#include "net/url_request/test_url_fetcher_factory.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -34,8 +33,7 @@ class ProfileOAuth2TokenServiceIOSDelegateTest public ProfileOAuth2TokenServiceObserver { public: ProfileOAuth2TokenServiceIOSDelegateTest() - : factory_(nullptr), - client_(&prefs_), + : client_(&prefs_), token_available_count_(0), token_revoked_count_(0), tokens_loaded_count_(0), @@ -54,8 +52,6 @@ class ProfileOAuth2TokenServiceIOSDelegateTest prefs::kTokenServiceExcludedSecondaryAccounts); fake_provider_ = new FakeDeviceAccountsProvider(); - factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_revoke_url(), "", - net::HTTP_OK, net::URLRequestStatus::SUCCESS); oauth2_delegate_.reset(new ProfileOAuth2TokenServiceIOSDelegate( &client_, base::WrapUnique(fake_provider_), &account_tracker_)); oauth2_delegate_->AddObserver(this); @@ -106,7 +102,6 @@ class ProfileOAuth2TokenServiceIOSDelegateTest protected: base::test::TaskEnvironment task_environment_; - net::FakeURLFetcherFactory factory_; TestingPrefServiceSimple prefs_; TestSigninClient client_; AccountTrackerService account_tracker_; diff --git a/chromium/components/signin/ios/OWNERS b/chromium/components/signin/ios/OWNERS index eaea7716829..04f4beaa435 100644 --- a/chromium/components/signin/ios/OWNERS +++ b/chromium/components/signin/ios/OWNERS @@ -1,3 +1,4 @@ +fernandex@chromium.org jlebel@chromium.org msarda@chromium.org diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm index 1febb6d9902..46e194e456e 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service.mm @@ -154,7 +154,7 @@ void AccountConsistencyHandler::WebStateDestroyed() { // Designated initializer. |callback| will be called every time a navigation has // finished. |callback| must not be empty. -- (instancetype)initWithCallback:(const base::Closure&)callback +- (instancetype)initWithCallback:(const base::RepeatingClosure&)callback NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -162,10 +162,10 @@ void AccountConsistencyHandler::WebStateDestroyed() { @implementation AccountConsistencyNavigationDelegate { // Callback that will be called every time a navigation has finished. - base::Closure _callback; + base::RepeatingClosure _callback; } -- (instancetype)initWithCallback:(const base::Closure&)callback { +- (instancetype)initWithCallback:(const base::RepeatingClosure&)callback { self = [super init]; if (self) { DCHECK(!callback.is_null()); @@ -426,9 +426,9 @@ WKWebView* AccountConsistencyService::GetWKWebView() { if (!web_view_) { web_view_ = BuildWKWebView(); navigation_delegate_ = [[AccountConsistencyNavigationDelegate alloc] - initWithCallback:base::Bind(&AccountConsistencyService:: - FinishedApplyingCookieRequest, - base::Unretained(this), true)]; + initWithCallback:base::BindRepeating(&AccountConsistencyService:: + FinishedApplyingCookieRequest, + base::Unretained(this), true)]; [web_view_ setNavigationDelegate:navigation_delegate_]; } return web_view_; diff --git a/chromium/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc b/chromium/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc index e23d22f65d3..c46fb6a5dd2 100644 --- a/chromium/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc +++ b/chromium/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc @@ -31,8 +31,8 @@ TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedImmediately) { network_change_notifier_->SetConnectionType( net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); callback_helper_.HandleCallback( - base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, - base::Unretained(this))); + base::BindOnce(&WaitForNetworkCallbackHelperTest::CallbackFunction, + base::Unretained(this))); EXPECT_EQ(1, num_callbacks_invoked_); } @@ -40,11 +40,11 @@ TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedLater) { network_change_notifier_->SetConnectionType( net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE); callback_helper_.HandleCallback( - base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, - base::Unretained(this))); + base::BindOnce(&WaitForNetworkCallbackHelperTest::CallbackFunction, + base::Unretained(this))); callback_helper_.HandleCallback( - base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, - base::Unretained(this))); + base::BindOnce(&WaitForNetworkCallbackHelperTest::CallbackFunction, + base::Unretained(this))); EXPECT_EQ(0, num_callbacks_invoked_); network_change_notifier_->SetConnectionType( diff --git a/chromium/components/signin/public/android/BUILD.gn b/chromium/components/signin/public/android/BUILD.gn new file mode 100644 index 00000000000..3212a290ec5 --- /dev/null +++ b/chromium/components/signin/public/android/BUILD.gn @@ -0,0 +1,38 @@ +import("//build/config/android/rules.gni") + +android_library("java") { + deps = [ + "//base:base_java", + "//base:jni_java", + "//components/signin/core/browser/android:java", + "//net/android:net_java", + "//third_party/android_deps:android_support_v4_java", + "//third_party/android_deps:androidx_annotation_annotation_java", + ] + + srcjar_deps = [ + "//components/signin/public/base:signin_metrics_enum_javagen", + "//components/signin/public/identity_manager:identity_manager_enum_javagen", + ] + + sources = [ + "java/src/org/chromium/components/signin/base/CoreAccountId.java", + "java/src/org/chromium/components/signin/base/CoreAccountInfo.java", + "java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", + "java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", + "java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java", + ] + + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] +} + +generate_jni("jni_headers") { + namespace = "signin" + sources = [ + "java/src/org/chromium/components/signin/base/CoreAccountId.java", + "java/src/org/chromium/components/signin/base/CoreAccountInfo.java", + "java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", + "java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", + "java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java", + ] +} diff --git a/chromium/components/signin/public/identity_manager/android/DEPS b/chromium/components/signin/public/android/DEPS index dba4bd1200e..93b627e9c9d 100644 --- a/chromium/components/signin/public/identity_manager/android/DEPS +++ b/chromium/components/signin/public/android/DEPS @@ -1,18 +1,20 @@ specific_include_rules = { "CoreAccountInfo.java": [ - "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java" + "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountUtils.java", ], "IdentityManager.java": [ "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java", ], - "OAuth2TokenService.java": [ - "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java", + "ProfileOAuth2TokenServiceDelegate.java": [ "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountTrackerService.java", - "+components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java", + "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java", + "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AuthException.java", + "+components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java", ], - "OAuth2TokenServiceTest.java": [ + "ProfileOAuth2TokenServiceDelegateTest.java": [ "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java", + "+components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", "+components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountHolder.java", "+components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/AccountManagerTestRule.java", ], diff --git a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountId.java b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/base/CoreAccountId.java index bd963cce960..bf4ee4df93a 100644 --- a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountId.java +++ b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/base/CoreAccountId.java @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.components.signin.identitymanager; +package org.chromium.components.signin.base; import androidx.annotation.NonNull; diff --git a/chromium/components/signin/public/android/java/src/org/chromium/components/signin/base/CoreAccountInfo.java b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/base/CoreAccountInfo.java new file mode 100644 index 00000000000..8d1cce892f1 --- /dev/null +++ b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/base/CoreAccountInfo.java @@ -0,0 +1,124 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.signin.base; + +import android.accounts.Account; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.components.signin.AccountUtils; + +/** + * Structure storing the core information about a Google account that is always known. The {@link + * CoreAccountInfo} for a given user is almost always the same but it may change in some rare cases. + * For example, the email will change if the user changes email. + * This class has a native counterpart called CoreAccountInfo. + */ +public class CoreAccountInfo { + private final CoreAccountId mId; + private final String mEmail; + private final String mGaiaId; + + /** + * Constructs a CoreAccountInfo with the provided parameters + * @param id A CoreAccountId associated with the account, equal to either email or gaiaId. + * @param email The email of the account. + * @param gaiaId String representation of the Gaia ID. Must not be an email address. + */ + @CalledByNative + public CoreAccountInfo( + @NonNull CoreAccountId id, @NonNull String email, @NonNull String gaiaId) { + assert id != null; + assert email != null; + assert gaiaId != null; + assert !gaiaId.contains("@"); + + mId = id; + mEmail = email; + mGaiaId = gaiaId; + } + + /** + * Returns a unique identifier of the current account. + */ + @CalledByNative + public CoreAccountId getId() { + return mId; + } + + /** + * Returns the email of the current account. + */ + @CalledByNative + public String getEmail() { + return mEmail; + } + + /** + * Returns the string representation of the Gaia ID + */ + @CalledByNative + public String getGaiaId() { + return mGaiaId; + } + + @Override + public String toString() { + return String.format("CoreAccountInfo{id[%s], name[%s]}", getId(), getEmail()); + } + + @Override + public int hashCode() { + int result = 31 * mId.hashCode() + mEmail.hashCode(); + return 31 * result + mGaiaId.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CoreAccountInfo)) return false; + CoreAccountInfo other = (CoreAccountInfo) obj; + return mId.equals(other.mId) && mEmail.equals(other.mEmail) + && mGaiaId.equals(other.mGaiaId); + } + + /** + * Null-checking helper to create {@link Account} from a possibly null {@link CoreAccountInfo}. + * + * @return {@link Account} for the argument if it is not null, null otherwise. + */ + public static @Nullable Account getAndroidAccountFrom(@Nullable CoreAccountInfo accountInfo) { + return accountInfo == null ? null + : AccountUtils.createAccountFromName(accountInfo.getEmail()); + } + + /** + * Null-checking helper to get an account id from a possibly null {@link CoreAccountInfo}. + * + * @return {@link #getId()} for the argument if it is not null, null otherwise. + */ + public static @Nullable CoreAccountId getIdFrom(@Nullable CoreAccountInfo accountInfo) { + return accountInfo == null ? null : accountInfo.getId(); + } + + /** + * Null-checking helper to get an email from a possibly null {@link CoreAccountInfo}. + * + * @return {@link #getEmail()} for the argument if it is not null, null otherwise. + */ + public static @Nullable String getEmailFrom(@Nullable CoreAccountInfo accountInfo) { + return accountInfo == null ? null : accountInfo.getEmail(); + } + + /** + * Null-checking helper to get a GaiaId from a possibly null {@link CoreAccountInfo}. + * + * @return {@link #getGaiaId()} ()} for the argument if it is not null, null otherwise. + */ + public static @Nullable String getGaiaIdFrom(@Nullable CoreAccountInfo accountInfo) { + return accountInfo == null ? null : accountInfo.getGaiaId(); + } +} diff --git a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityManager.java b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/IdentityManager.java index ce35cfd6199..1fdcf187457 100644 --- a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityManager.java +++ b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/IdentityManager.java @@ -5,15 +5,16 @@ package org.chromium.components.signin.identitymanager; import android.accounts.Account; -import android.support.annotation.MainThread; -import android.support.annotation.Nullable; +import androidx.annotation.MainThread; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import org.chromium.base.ObserverList; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.NativeMethods; import org.chromium.components.signin.AccountManagerFacade; +import org.chromium.components.signin.base.CoreAccountInfo; /** * IdentityManager provides access to native IdentityManager's public API to java components. @@ -42,10 +43,11 @@ public class IdentityManager { /** * A simple callback for getAccessToken. */ - public interface GetAccessTokenCallback extends OAuth2TokenService.GetAccessTokenCallback {} + public interface GetAccessTokenCallback + extends ProfileOAuth2TokenServiceDelegate.GetAccessTokenCallback {} private long mNativeIdentityManager; - private OAuth2TokenService mOAuth2TokenService; + private ProfileOAuth2TokenServiceDelegate mProfileOAuth2TokenServiceDelegate; private final ObserverList<Observer> mObservers = new ObserverList<>(); @@ -53,16 +55,17 @@ public class IdentityManager { * Called by native to create an instance of IdentityManager. */ @CalledByNative - private static IdentityManager create( - long nativeIdentityManager, OAuth2TokenService oAuth2TokenService) { + private static IdentityManager create(long nativeIdentityManager, + ProfileOAuth2TokenServiceDelegate profileOAuth2TokenServiceDelegate) { assert nativeIdentityManager != 0; - return new IdentityManager(nativeIdentityManager, oAuth2TokenService); + return new IdentityManager(nativeIdentityManager, profileOAuth2TokenServiceDelegate); } @VisibleForTesting - public IdentityManager(long nativeIdentityManager, OAuth2TokenService oAuth2TokenService) { + public IdentityManager(long nativeIdentityManager, + ProfileOAuth2TokenServiceDelegate profileOAuth2TokenServiceDelegate) { mNativeIdentityManager = nativeIdentityManager; - mOAuth2TokenService = oAuth2TokenService; + mProfileOAuth2TokenServiceDelegate = profileOAuth2TokenServiceDelegate; } /** @@ -112,7 +115,7 @@ public class IdentityManager { * Returns whether the user's primary account is available. */ public boolean hasPrimaryAccount() { - return IdentityManagerJni.get().hasPrimaryAccount(mNativeIdentityManager); + return getPrimaryAccountInfo() != null; } /** @@ -133,14 +136,6 @@ public class IdentityManager { } /** - * Provides access to the account ID of the user's primary account. Returns null if no such info - * is available. - */ - public @Nullable CoreAccountId getPrimaryAccountId() { - return IdentityManagerJni.get().getPrimaryAccountId(mNativeIdentityManager); - } - - /** * Looks up and returns information for account with given |email_address|. If the account * cannot be found, return a null value. */ @@ -160,9 +155,9 @@ public class IdentityManager { */ @MainThread public void getAccessToken(Account account, String scope, GetAccessTokenCallback callback) { - assert mOAuth2TokenService != null; + assert mProfileOAuth2TokenServiceDelegate != null; // TODO(crbug.com/934688) The following should call a JNI method instead. - mOAuth2TokenService.getAccessToken(account, scope, callback); + mProfileOAuth2TokenServiceDelegate.getAccessToken(account, scope, callback); } /** @@ -182,7 +177,8 @@ public class IdentityManager { public static void getAccessTokenWithFacade(AccountManagerFacade accountManagerFacade, Account account, String scope, GetAccessTokenCallback callback) { // TODO(crbug.com/934688) The following should call a JNI method instead. - OAuth2TokenService.getAccessTokenWithFacade(accountManagerFacade, account, scope, callback); + ProfileOAuth2TokenServiceDelegate.getAccessTokenWithFacade( + accountManagerFacade, account, scope, callback); } /** @@ -191,10 +187,10 @@ public class IdentityManager { */ @MainThread public void invalidateAccessToken(String accessToken) { - assert mOAuth2TokenService != null; + assert mProfileOAuth2TokenServiceDelegate != null; // TODO(crbug.com/934688) The following should call a JNI method instead. - mOAuth2TokenService.invalidateAccessToken(accessToken); + mProfileOAuth2TokenServiceDelegate.invalidateAccessToken(accessToken); } /** @@ -213,18 +209,17 @@ public class IdentityManager { public static void getNewAccessTokenWithFacade(AccountManagerFacade accountManagerFacade, Account account, @Nullable String oldToken, String scope, GetAccessTokenCallback callback) { - OAuth2TokenService.getNewAccessTokenWithFacade( + ProfileOAuth2TokenServiceDelegate.getNewAccessTokenWithFacade( accountManagerFacade, account, oldToken, scope, callback); } @NativeMethods - interface Natives { - public @Nullable CoreAccountInfo getPrimaryAccountInfo(long nativeIdentityManager); - public @Nullable CoreAccountId getPrimaryAccountId(long nativeIdentityManager); - public boolean hasPrimaryAccount(long nativeIdentityManager); - public @Nullable CoreAccountInfo - findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( + public interface Natives { + @Nullable + CoreAccountInfo getPrimaryAccountInfo(long nativeIdentityManager); + @Nullable + CoreAccountInfo findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( long nativeIdentityManager, String email); - public CoreAccountInfo[] getAccountsWithRefreshTokens(long nativeIdentityManager); + CoreAccountInfo[] getAccountsWithRefreshTokens(long nativeIdentityManager); } } diff --git a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java index 45f09282836..79974fe1539 100644 --- a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java +++ b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java @@ -8,6 +8,7 @@ import androidx.annotation.Nullable; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.signin.base.CoreAccountId; import org.chromium.components.signin.metrics.SignoutDelete; import org.chromium.components.signin.metrics.SignoutReason; diff --git a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/OAuth2TokenService.java b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java index 00c1ea72897..d5947bec72d 100644 --- a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/OAuth2TokenService.java +++ b/chromium/components/signin/public/android/java/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegate.java @@ -11,7 +11,6 @@ import androidx.annotation.MainThread; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.StrictModeContext; import org.chromium.base.ThreadUtils; @@ -19,31 +18,27 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.task.AsyncTask; import org.chromium.components.signin.AccountManagerFacade; +import org.chromium.components.signin.AccountManagerFacadeProvider; import org.chromium.components.signin.AccountTrackerService; import org.chromium.components.signin.AuthException; import org.chromium.net.NetworkChangeNotifier; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** - * Java instance for the native OAuth2TokenService. + * Java instance for the native ProfileOAuth2TokenServiceDelegate. * <p/> * This class forwards calls to request or invalidate access tokens made by native code to * AccountManagerFacade and forwards callbacks to native code. * <p/> */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) -public final class OAuth2TokenService +public final class ProfileOAuth2TokenServiceDelegate implements AccountTrackerService.OnSystemAccountsSeededListener { private static final String TAG = "OAuth2TokenService"; - public static final String STORED_ACCOUNTS_KEY = "google.services.stored_accounts"; - /** * A simple callback for getAccessToken. */ @@ -66,20 +61,20 @@ public final class OAuth2TokenService private static final String OAUTH2_SCOPE_PREFIX = "oauth2:"; - private final long mNativeOAuth2TokenServiceDelegate; + private final long mNativeProfileOAuth2TokenServiceDelegate; private final AccountTrackerService mAccountTrackerService; private final AccountManagerFacade mAccountManagerFacade; private boolean mPendingUpdate; - // TODO(crbug.com/934688) Once OAuth2TokenService.java is internalized, use CoreAccountId - // instead of String. + // TODO(crbug.com/934688) Once ProfileOAuth2TokenServiceDelegate.java is internalized, use + // CoreAccountId instead of String. private String mPendingUpdateAccountId; @VisibleForTesting - public OAuth2TokenService(long nativeOAuth2TokenServiceDelegate, + ProfileOAuth2TokenServiceDelegate(long nativeProfileOAuth2TokenServiceDelegateDelegate, AccountTrackerService accountTrackerService, AccountManagerFacade accountManagerFacade) { - mNativeOAuth2TokenServiceDelegate = nativeOAuth2TokenServiceDelegate; + mNativeProfileOAuth2TokenServiceDelegate = nativeProfileOAuth2TokenServiceDelegateDelegate; mAccountTrackerService = accountTrackerService; mAccountManagerFacade = accountManagerFacade; @@ -90,12 +85,14 @@ public final class OAuth2TokenService } @CalledByNative - private static OAuth2TokenService create(long nativeOAuth2TokenServiceDelegate, + private static ProfileOAuth2TokenServiceDelegate create( + long nativeProfileOAuth2TokenServiceDelegateDelegate, AccountTrackerService accountTrackerService, AccountManagerFacade accountManagerFacade) { - assert nativeOAuth2TokenServiceDelegate != 0; - return new OAuth2TokenService( - nativeOAuth2TokenServiceDelegate, accountTrackerService, accountManagerFacade); + assert nativeProfileOAuth2TokenServiceDelegateDelegate != 0; + return new ProfileOAuth2TokenServiceDelegate( + nativeProfileOAuth2TokenServiceDelegateDelegate, accountTrackerService, + accountManagerFacade); } private Account getAccountOrNullFromUsername(String username) { @@ -118,8 +115,8 @@ public final class OAuth2TokenService @CalledByNative @VisibleForTesting String[] getSystemAccountNames() { - // TODO(https://crbug.com/768366): Remove this after adding cache to account manager facade. - // This function is called by native code on UI thread. + // TODO(https://crbug.com/768366): Remove this after adding cache to account manager + // facade. This function is called by native code on UI thread. try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { List<String> accountNames = mAccountManagerFacade.tryGetGoogleAccountNames(); return accountNames.toArray(new String[accountNames.size()]); @@ -127,22 +124,11 @@ public final class OAuth2TokenService } /** - * Called by native to list the accounts Id with OAuth2 refresh tokens. - * This can differ from getSystemAccountNames as the user add/remove accounts - * from the OS. updateAccountList should be called to keep these two - * in sync. - */ - @CalledByNative - @VisibleForTesting - static String[] getAccounts() { - return getStoredAccounts(); - } - - /** * Called by native to retrieve OAuth2 tokens. * @param username The native username (email address). * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). - * @param nativeCallback The pointer to the native callback that should be run upon completion. + * @param nativeCallback The pointer to the native callback that should be run upon + * completion. */ @MainThread @CalledByNative @@ -151,7 +137,8 @@ public final class OAuth2TokenService Account account = getAccountOrNullFromUsername(username); if (account == null) { ThreadUtils.postOnUiThread(() -> { - OAuth2TokenServiceJni.get().onOAuth2TokenFetched(null, false, nativeCallback); + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( + null, false, nativeCallback); }); return; } @@ -159,20 +146,21 @@ public final class OAuth2TokenService getAccessToken(account, oauth2Scope, new GetAccessTokenCallback() { @Override public void onGetTokenSuccess(String token) { - OAuth2TokenServiceJni.get().onOAuth2TokenFetched(token, false, nativeCallback); + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( + token, false, nativeCallback); } @Override public void onGetTokenFailure(boolean isTransientError) { - OAuth2TokenServiceJni.get().onOAuth2TokenFetched( + ProfileOAuth2TokenServiceDelegateJni.get().onOAuth2TokenFetched( null, isTransientError, nativeCallback); } }); } /** - * Call this method to retrieve an OAuth2 access token for the given account and scope. Please - * note that this method expects a scope with 'oauth2:' prefix. + * Call this method to retrieve an OAuth2 access token for the given account and scope. + * Please note that this method expects a scope with 'oauth2:' prefix. * @param account the account to get the access token for. * @param scope The scope to get an auth token for (with Android-style 'oauth2:' prefix). * @param callback called on successful and unsuccessful fetching of auth token. @@ -283,10 +271,11 @@ public final class OAuth2TokenService } // Temporarily allowing disk read while fixing. TODO: http://crbug.com/618096. - // This function is called in RefreshTokenIsAvailable of OAuth2TokenService which is - // expected to be called in the UI thread synchronously. + // This function is called in RefreshTokenIsAvailable of + // ProfileOAuth2TokenServiceDelegate which is expected to be called in the UI thread + // synchronously. try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { - return AccountManagerFacade.get().hasAccountForName(accountName); + return AccountManagerFacadeProvider.getInstance().hasAccountForName(accountName); } } @@ -317,28 +306,8 @@ public final class OAuth2TokenService } private void reloadAllAccountsWithPrimaryAccountAfterSeeding(@Nullable String accountId) { - OAuth2TokenServiceJni.get().reloadAllAccountsWithPrimaryAccountAfterSeeding( - mNativeOAuth2TokenServiceDelegate, accountId); - } - - private static String[] getStoredAccounts() { - Set<String> accounts = - ContextUtils.getAppSharedPreferences().getStringSet(STORED_ACCOUNTS_KEY, null); - return accounts == null ? new String[] {} : accounts.toArray(new String[0]); - } - - /** - * 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. - */ - @CalledByNative - private static void setAccounts(String[] accounts) { - Set<String> set = new HashSet<>(Arrays.asList(accounts)); - ContextUtils.getAppSharedPreferences() - .edit() - .putStringSet(STORED_ACCOUNTS_KEY, set) - .apply(); + ProfileOAuth2TokenServiceDelegateJni.get().reloadAllAccountsWithPrimaryAccountAfterSeeding( + mNativeProfileOAuth2TokenServiceDelegate, accountId); } private interface AuthTask<T> { @@ -350,8 +319,8 @@ public final class OAuth2TokenService /** * A helper class to encapsulate network connection retry logic for AuthTasks. * - * The task will be run on the background thread. If it encounters a transient error, it will - * wait for a network change and retry up to MAX_TRIES times. + * The task will be run on the background thread. If it encounters a transient error, it + * will wait for a network change and retry up to MAX_TRIES times. */ private static class ConnectionRetry<T> implements NetworkChangeNotifier.ConnectionTypeObserver { @@ -423,6 +392,6 @@ public final class OAuth2TokenService interface Natives { void onOAuth2TokenFetched(String authToken, boolean isTransientError, long nativeCallback); void reloadAllAccountsWithPrimaryAccountAfterSeeding( - long nativeOAuth2TokenServiceDelegateAndroid, @Nullable String accountId); + long nativeProfileOAuth2TokenServiceDelegateAndroid, @Nullable String accountId); } } diff --git a/chromium/components/signin/public/identity_manager/android/javatests/src/org/chromium/components/signin/identitymanager/OAuth2TokenServiceTest.java b/chromium/components/signin/public/android/javatests/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java index 69838c18a1e..4b200a921a3 100644 --- a/chromium/components/signin/public/identity_manager/android/javatests/src/org/chromium/components/signin/identitymanager/OAuth2TokenServiceTest.java +++ b/chromium/components/signin/public/android/javatests/src/org/chromium/components/signin/identitymanager/ProfileOAuth2TokenServiceDelegateTest.java @@ -20,26 +20,28 @@ import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.AdvancedMockContext; import org.chromium.base.test.util.Feature; import org.chromium.components.signin.AccountManagerFacade; +import org.chromium.components.signin.AccountManagerFacadeProvider; import org.chromium.components.signin.test.util.AccountHolder; import org.chromium.components.signin.test.util.AccountManagerTestRule; import java.util.Arrays; import java.util.concurrent.CountDownLatch; -/** Tests for OAuth2TokenService. */ +/** Tests for ProfileOAuth2TokenServiceDelegate. */ @RunWith(BaseJUnit4ClassRunner.class) -public class OAuth2TokenServiceTest { +public class ProfileOAuth2TokenServiceDelegateTest { private AdvancedMockContext mContext; @Rule public AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule(); - private OAuth2TokenService mOAuth2TokenService; + private ProfileOAuth2TokenServiceDelegate mProfileOAuth2TokenServiceDelegate; /** * Class handling GetAccessToken callbacks and providing a blocking {@link * #getToken()}. */ - class GetAccessTokenCallbackForTest implements OAuth2TokenService.GetAccessTokenCallback { + class GetAccessTokenCallbackForTest + implements ProfileOAuth2TokenServiceDelegate.GetAccessTokenCallback { private String mToken; final CountDownLatch mTokenRetrievedCountDown = new CountDownLatch(1); @@ -72,20 +74,21 @@ public class OAuth2TokenServiceTest { @Before public void setUp() { mContext = new AdvancedMockContext(InstrumentationRegistry.getTargetContext()); - mOAuth2TokenService = new OAuth2TokenService(0 /*nativeOAuth2TokenServiceDelegate*/, - null /* AccountTrackerService */, AccountManagerFacade.get()); + mProfileOAuth2TokenServiceDelegate = new ProfileOAuth2TokenServiceDelegate( + 0 /*nativeProfileOAuth2TokenServiceDelegateDelegate*/, + null /* AccountTrackerService */, AccountManagerFacadeProvider.get()); } @After public void tearDown() { - AccountManagerFacade.resetAccountManagerFacadeForTests(); + AccountManagerFacadeProvider.resetAccountManagerFacadeForTests(); } @Test @SmallTest @Feature({"Sync"}) public void testGetAccountsNoAccountsRegistered() { - String[] accounts = OAuth2TokenService.getAccounts(); + String[] accounts = ProfileOAuth2TokenServiceDelegate.getAccounts(); Assert.assertEquals("There should be no accounts registered", 0, accounts.length); } @@ -97,11 +100,11 @@ public class OAuth2TokenServiceTest { AccountHolder accountHolder1 = AccountHolder.builder(account1).build(); mAccountManagerTestRule.addAccount(accountHolder1); - String[] sysAccounts = mOAuth2TokenService.getSystemAccountNames(); + String[] sysAccounts = mProfileOAuth2TokenServiceDelegate.getSystemAccountNames(); Assert.assertEquals("There should be one registered account", 1, sysAccounts.length); Assert.assertEquals("The account should be " + account1, account1.name, sysAccounts[0]); - String[] accounts = OAuth2TokenService.getAccounts(); + String[] accounts = ProfileOAuth2TokenServiceDelegate.getAccounts(); Assert.assertEquals("There should be zero registered account", 0, accounts.length); } @@ -116,14 +119,14 @@ public class OAuth2TokenServiceTest { AccountHolder accountHolder2 = AccountHolder.builder(account2).build(); mAccountManagerTestRule.addAccount(accountHolder2); - String[] sysAccounts = mOAuth2TokenService.getSystemAccountNames(); + String[] sysAccounts = mProfileOAuth2TokenServiceDelegate.getSystemAccountNames(); Assert.assertEquals("There should be one registered account", 2, sysAccounts.length); Assert.assertTrue("The list should contain " + account1, Arrays.asList(sysAccounts).contains(account1.name)); Assert.assertTrue("The list should contain " + account2, Arrays.asList(sysAccounts).contains(account2.name)); - String[] accounts = OAuth2TokenService.getAccounts(); + String[] accounts = ProfileOAuth2TokenServiceDelegate.getAccounts(); Assert.assertEquals("There should be zero registered account", 0, accounts.length); } @@ -157,8 +160,9 @@ public class OAuth2TokenServiceTest { mAccountManagerTestRule.addAccount(accountHolder); GetAccessTokenCallbackForTest callback = new GetAccessTokenCallbackForTest(); - ThreadUtils.runOnUiThreadBlocking( - () -> { mOAuth2TokenService.getAccessToken(account, scope, callback); }); + ThreadUtils.runOnUiThreadBlocking(() -> { + mProfileOAuth2TokenServiceDelegate.getAccessToken(account, scope, callback); + }); Assert.assertEquals(expectedToken, callback.getToken()); } diff --git a/chromium/components/signin/public/base/BUILD.gn b/chromium/components/signin/public/base/BUILD.gn index dea58bf1bb6..80f8a7229e5 100644 --- a/chromium/components/signin/public/base/BUILD.gn +++ b/chromium/components/signin/public/base/BUILD.gn @@ -27,6 +27,8 @@ static_library("base") { "device_id_helper.h", "multilogin_parameters.cc", "multilogin_parameters.h", + "persistent_repeating_timer.cc", + "persistent_repeating_timer.h", "signin_client.cc", "signin_client.h", "signin_metrics.cc", @@ -56,9 +58,7 @@ static_library("base") { if (is_android) { java_cpp_enum("signin_metrics_enum_javagen") { - sources = [ - "signin_metrics.h", - ] + sources = [ "signin_metrics.h" ] } } @@ -89,6 +89,7 @@ source_set("unit_tests") { sources = [ "avatar_icon_util_unittest.cc", "device_id_helper_unittest.cc", + "persistent_repeating_timer_unittest.cc", "signin_metrics_unittest.cc", ] @@ -96,6 +97,7 @@ source_set("unit_tests") { ":base", "//base", "//base/test:test_support", + "//components/prefs:test_support", "//components/sync_preferences:test_support", "//testing/gtest", "//url", diff --git a/chromium/components/signin/public/base/account_consistency_method.cc b/chromium/components/signin/public/base/account_consistency_method.cc index c278657ef49..09b96305c94 100644 --- a/chromium/components/signin/public/base/account_consistency_method.cc +++ b/chromium/components/signin/public/base/account_consistency_method.cc @@ -9,8 +9,8 @@ namespace signin { #if defined(OS_ANDROID) -const base::Feature kMiceFeature{"MobileIdentityConsistency", - base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kMobileIdentityConsistency{ + "MobileIdentityConsistency", base::FEATURE_DISABLED_BY_DEFAULT}; #endif } // namespace signin diff --git a/chromium/components/signin/public/base/account_consistency_method.h b/chromium/components/signin/public/base/account_consistency_method.h index 88786f1766c..2750d8a7811 100644 --- a/chromium/components/signin/public/base/account_consistency_method.h +++ b/chromium/components/signin/public/base/account_consistency_method.h @@ -17,12 +17,9 @@ namespace signin { #if defined(OS_ANDROID) // Mice is similar to Mirror but also works when the user is not opted into // Sync. -extern const base::Feature kMiceFeature; +extern const base::Feature kMobileIdentityConsistency; #endif -// TODO(https://crbug.com/777774): Cleanup this enum and remove related -// functions once Dice is fully rolled out, and/or Mirror code is removed on -// desktop. enum class AccountConsistencyMethod : int { // No account consistency. kDisabled, diff --git a/chromium/components/signin/public/base/persistent_repeating_timer.cc b/chromium/components/signin/public/base/persistent_repeating_timer.cc new file mode 100644 index 00000000000..cb5dada2c9b --- /dev/null +++ b/chromium/components/signin/public/base/persistent_repeating_timer.cc @@ -0,0 +1,54 @@ +// Copyright 2020 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/public/base/persistent_repeating_timer.h" + +#include "base/callback.h" +#include "components/prefs/pref_service.h" + +namespace signin { + +PersistentRepeatingTimer::PersistentRepeatingTimer( + PrefService* prefs, + const char* timer_last_update_pref_name, + base::TimeDelta delay, + base::RepeatingClosure task) + : prefs_(prefs), + last_fired_pref_name_(timer_last_update_pref_name), + delay_(delay), + user_task_(task) {} + +PersistentRepeatingTimer::~PersistentRepeatingTimer() = default; + +void PersistentRepeatingTimer::Start() { + if (timer_.IsRunning()) + return; // Already started. + + const base::TimeDelta time_since_update = base::Time::Now() - GetLastFired(); + if (time_since_update >= delay_) { + OnTimerFired(); + } else { + timer_.Start(FROM_HERE, delay_ - time_since_update, + base::Bind(&PersistentRepeatingTimer::OnTimerFired, + base::Unretained(this))); + } + DCHECK(timer_.IsRunning()); +} + +base::Time PersistentRepeatingTimer::GetLastFired() { + return prefs_->GetTime(last_fired_pref_name_); +} + +void PersistentRepeatingTimer::SetLastFiredNow() { + prefs_->SetTime(last_fired_pref_name_, base::Time::Now()); +} + +void PersistentRepeatingTimer::OnTimerFired() { + DCHECK(!timer_.IsRunning()); + SetLastFiredNow(); + user_task_.Run(); + Start(); +} + +} // namespace signin diff --git a/chromium/components/signin/public/base/persistent_repeating_timer.h b/chromium/components/signin/public/base/persistent_repeating_timer.h new file mode 100644 index 00000000000..df65983c3ea --- /dev/null +++ b/chromium/components/signin/public/base/persistent_repeating_timer.h @@ -0,0 +1,54 @@ +// Copyright 2020 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_PUBLIC_BASE_PERSISTENT_REPEATING_TIMER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_PERSISTENT_REPEATING_TIMER_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/time/time.h" +#include "base/timer/timer.h" + +class PrefService; + +namespace signin { + +// This class fires a task repeatedly, across application restarts. The timer +// stores the date of the last invocation in a preference, which is persisted +// to disk. +class PersistentRepeatingTimer { + public: + // The timer is not started at creation. + PersistentRepeatingTimer(PrefService* prefs, + const char* timer_last_update_pref_name, + base::TimeDelta delay, + base::RepeatingClosure task); + + ~PersistentRepeatingTimer(); + + // Starts the timer. Calling Start() when the timer is running has no effect. + void Start(); + + private: + // Reads the date of the last event from the pref. + base::Time GetLastFired(); + + // Updates the pref with the current time. + void SetLastFiredNow(); + + // Called when |timer_| fires. + void OnTimerFired(); + + PrefService* prefs_; + std::string last_fired_pref_name_; + base::TimeDelta delay_; + base::RepeatingClosure user_task_; + + base::RetainingOneShotTimer timer_; +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_PERSISTENT_REPEATING_TIMER_H_ diff --git a/chromium/components/signin/public/base/persistent_repeating_timer_unittest.cc b/chromium/components/signin/public/base/persistent_repeating_timer_unittest.cc new file mode 100644 index 00000000000..29783bde3be --- /dev/null +++ b/chromium/components/signin/public/base/persistent_repeating_timer_unittest.cc @@ -0,0 +1,137 @@ +// Copyright 2020 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/public/base/persistent_repeating_timer.h" + +#include "base/test/task_environment.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace signin { + +namespace { + +const char kLastUpdatedTimePref[] = "test.last_updated_time"; +constexpr base::TimeDelta kTestDelay = base::TimeDelta::FromHours(2); + +} // namespace + +class PersistentRepeatingTimerTest : public ::testing::Test { + public: + PersistentRepeatingTimerTest() { + pref_service_.registry()->RegisterTimePref(kLastUpdatedTimePref, + base::Time()); + } + + void RunTask() { ++call_count_; } + + void CheckCallCount(int call_count) { + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(call_count, call_count_); + } + + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + TestingPrefServiceSimple pref_service_; + int call_count_ = 0; +}; + +// Checks that the missing pref is treated like an old one. +TEST_F(PersistentRepeatingTimerTest, MissingPref) { + PersistentRepeatingTimer timer( + &pref_service_, kLastUpdatedTimePref, kTestDelay, + base::Bind(&PersistentRepeatingTimerTest::RunTask, + base::Unretained(this))); + CheckCallCount(0); + + // The task is run immediately on start. + timer.Start(); + CheckCallCount(1); + + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + CheckCallCount(1); + + // And after the delay. + task_environment_.FastForwardBy(kTestDelay); + CheckCallCount(2); +} + +// Checks that spurious calls to Start() have no effect. +TEST_F(PersistentRepeatingTimerTest, MultipleStarts) { + PersistentRepeatingTimer timer( + &pref_service_, kLastUpdatedTimePref, kTestDelay, + base::Bind(&PersistentRepeatingTimerTest::RunTask, + base::Unretained(this))); + CheckCallCount(0); + + // The task is run immediately on start. + timer.Start(); + CheckCallCount(1); + timer.Start(); + CheckCallCount(1); + + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + CheckCallCount(1); + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + timer.Start(); + CheckCallCount(1); + + // And after the delay. + task_environment_.FastForwardBy(kTestDelay); + CheckCallCount(2); + timer.Start(); + CheckCallCount(2); +} + +TEST_F(PersistentRepeatingTimerTest, RecentPref) { + pref_service_.SetTime(kLastUpdatedTimePref, + base::Time::Now() - base::TimeDelta::FromHours(1)); + + PersistentRepeatingTimer timer( + &pref_service_, kLastUpdatedTimePref, kTestDelay, + base::Bind(&PersistentRepeatingTimerTest::RunTask, + base::Unretained(this))); + CheckCallCount(0); + + // The task is NOT run immediately on start. + timer.Start(); + CheckCallCount(0); + + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + CheckCallCount(0); + + // It is run after te delay. + task_environment_.FastForwardBy(base::TimeDelta::FromHours(1)); + CheckCallCount(1); + task_environment_.FastForwardBy(base::TimeDelta::FromHours(1)); + CheckCallCount(1); + + task_environment_.FastForwardBy(base::TimeDelta::FromHours(1)); + CheckCallCount(2); +} + +TEST_F(PersistentRepeatingTimerTest, OldPref) { + pref_service_.SetTime(kLastUpdatedTimePref, + base::Time::Now() - base::TimeDelta::FromHours(10)); + + PersistentRepeatingTimer timer( + &pref_service_, kLastUpdatedTimePref, kTestDelay, + base::Bind(&PersistentRepeatingTimerTest::RunTask, + base::Unretained(this))); + CheckCallCount(0); + + // The task is run immediately on start. + timer.Start(); + CheckCallCount(1); + + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + CheckCallCount(1); + + // And after the delay. + task_environment_.FastForwardBy(kTestDelay); + CheckCallCount(2); +} + +} // namespace signin diff --git a/chromium/components/signin/public/base/signin_pref_names.cc b/chromium/components/signin/public/base/signin_pref_names.cc index 3d2a442d235..f935746e783 100644 --- a/chromium/components/signin/public/base/signin_pref_names.cc +++ b/chromium/components/signin/public/base/signin_pref_names.cc @@ -14,6 +14,11 @@ namespace prefs { // Account Manager. const char kAccountConsistencyMirrorRequired[] = "account_consistency_mirror.required"; + +// A boolean pref - should unauthenticated user should be logged out +// automatically. Default value is false. +const char kForceLogoutUnauthenticatedUserEnabled[] = + "profile.force_logout_unauthenticated_user_enabled"; #endif // An integer property indicating the state of account id migration from diff --git a/chromium/components/signin/public/base/signin_pref_names.h b/chromium/components/signin/public/base/signin_pref_names.h index 1db578bdae0..fe4af3e4432 100644 --- a/chromium/components/signin/public/base/signin_pref_names.h +++ b/chromium/components/signin/public/base/signin_pref_names.h @@ -9,6 +9,7 @@ namespace prefs { #if defined(OS_CHROMEOS) extern const char kAccountConsistencyMirrorRequired[]; +extern const char kForceLogoutUnauthenticatedUserEnabled[]; #endif extern const char kAccountIdMigrationState[]; extern const char kAccountInfo[]; diff --git a/chromium/components/signin/public/base/signin_switches.cc b/chromium/components/signin/public/base/signin_switches.cc index fb905755f84..8fdcf18fb67 100644 --- a/chromium/components/signin/public/base/signin_switches.cc +++ b/chromium/components/signin/public/base/signin_switches.cc @@ -13,17 +13,12 @@ const char kClearTokenService[] = "clear-token-service"; // Disables sending signin scoped device id to LSO with refresh token request. const char kDisableSigninScopedDeviceId[] = "disable-signin-scoped-device-id"; -#if !BUILDFLAG(ENABLE_MIRROR) -// Command line flag for enabling account consistency. Default mode is disabled. -// Mirror is a legacy mode in which Google accounts are always addded to Chrome, -// and Chrome then adds them to the Google authentication cookies. -// Dice is a new experiment in which Chrome is aware of the accounts in the -// Google authentication cookies. -const char kAccountConsistency[] = "account-consistency"; - -// Values for the kAccountConsistency flag. -const char kAccountConsistencyMirror[] = "mirror"; -const char kAccountConsistencyDice[] = "dice"; +#if defined(OS_CHROMEOS) +const base::Feature kAccountIdMigration{"AccountIdMigration", + base::FEATURE_DISABLED_BY_DEFAULT}; #endif +const base::Feature kOAuthRemoteConsent{"OAuthRemoteConsent", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace switches diff --git a/chromium/components/signin/public/base/signin_switches.h b/chromium/components/signin/public/base/signin_switches.h index 1397cbf4a40..d354b0a757d 100644 --- a/chromium/components/signin/public/base/signin_switches.h +++ b/chromium/components/signin/public/base/signin_switches.h @@ -5,6 +5,7 @@ #ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ #define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ +#include "base/feature_list.h" #include "components/signin/public/base/signin_buildflags.h" namespace switches { @@ -18,14 +19,13 @@ namespace switches { extern const char kClearTokenService[]; extern const char kDisableSigninScopedDeviceId[]; -#if !BUILDFLAG(ENABLE_MIRROR) -// Note: Account consistency (Mirror) is already enabled on mobile platforms, so -// this switch only exist on desktop platforms. -extern const char kAccountConsistency[]; -extern const char kAccountConsistencyMirror[]; -extern const char kAccountConsistencyDice[]; +#if defined(OS_CHROMEOS) +extern const base::Feature kAccountIdMigration; #endif +// Enables the remote consent flow for chrome.identity extension API. +extern const base::Feature kOAuthRemoteConsent; + } // namespace switches #endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ diff --git a/chromium/components/signin/public/identity_manager/BUILD.gn b/chromium/components/signin/public/identity_manager/BUILD.gn index 537e423ee9e..2e4dde69400 100644 --- a/chromium/components/signin/public/identity_manager/BUILD.gn +++ b/chromium/components/signin/public/identity_manager/BUILD.gn @@ -18,6 +18,7 @@ source_set("identity_manager") { "accounts_in_cookie_jar_info.cc", "accounts_in_cookie_jar_info.h", "accounts_mutator.h", + "consent_level.h", "device_accounts_synchronizer.h", "diagnostics_provider.h", "identity_manager.cc", @@ -32,6 +33,7 @@ source_set("identity_manager") { "primary_account_access_token_fetcher.cc", "primary_account_access_token_fetcher.h", "primary_account_mutator.h", + "scope_set.h", "set_accounts_in_cookie_result.h", "ubertoken_fetcher.cc", "ubertoken_fetcher.h", @@ -45,7 +47,6 @@ source_set("identity_manager") { "//components/signin/public/base", "//components/signin/public/base:signin_buildflags", "//google_apis", - "//services/identity/public/cpp:cpp_types", "//ui/gfx", ] @@ -66,8 +67,7 @@ source_set("identity_manager") { } if (is_android) { - deps += - [ "//components/signin/internal/identity_manager/android:jni_headers" ] + deps += [ "//components/signin/public/android:jni_headers" ] } allow_circular_includes_from = [ @@ -79,9 +79,7 @@ source_set("identity_manager") { if (is_android) { java_cpp_enum("identity_manager_enum_javagen") { - sources = [ - "primary_account_mutator.h", - ] + sources = [ "primary_account_mutator.h" ] } } diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher.cc b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc index df7671a2efb..4ed181b04f4 100644 --- a/chromium/components/signin/public/identity_manager/access_token_fetcher.cc +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc @@ -16,7 +16,7 @@ namespace signin { AccessTokenFetcher::AccessTokenFetcher(const CoreAccountId& account_id, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode) : AccessTokenFetcher(account_id, @@ -32,7 +32,7 @@ AccessTokenFetcher::AccessTokenFetcher( const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode) : AccessTokenFetcher(account_id, @@ -50,7 +50,7 @@ AccessTokenFetcher::AccessTokenFetcher(const CoreAccountId& account_id, const std::string client_secret, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode) : AccessTokenFetcher(account_id, @@ -70,7 +70,7 @@ AccessTokenFetcher::AccessTokenFetcher( const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode) : OAuth2AccessTokenManager::Consumer(oauth_consumer_name), diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher.h b/chromium/components/signin/public/identity_manager/access_token_fetcher.h index 2f588f41488..fdfecbe4232 100644 --- a/chromium/components/signin/public/identity_manager/access_token_fetcher.h +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher.h @@ -14,9 +14,9 @@ #include "base/scoped_observer.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_observer.h" +#include "components/signin/public/identity_manager/scope_set.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/oauth2_access_token_manager.h" -#include "services/identity/public/cpp/scope_set.h" namespace network { class SharedURLLoaderFactory; @@ -80,7 +80,7 @@ struct AccessTokenInfo; // // wrapper API surfaces. // MyClass::StartAccessTokenRequestForAccount(CoreAccountId account_id) { // // Choose scopes to obtain for the access token. -// identity::ScopeSet scopes; +// ScopeSet scopes; // scopes.insert(GaiaConstants::kMyFirstScope); // scopes.insert(GaiaConstants::kMySecondScope); @@ -165,7 +165,7 @@ class AccessTokenFetcher : public ProfileOAuth2TokenServiceObserver, AccessTokenFetcher(const CoreAccountId& account_id, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode); @@ -179,7 +179,7 @@ class AccessTokenFetcher : public ProfileOAuth2TokenServiceObserver, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode); @@ -191,7 +191,7 @@ class AccessTokenFetcher : public ProfileOAuth2TokenServiceObserver, const std::string client_secret, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode); @@ -205,7 +205,7 @@ class AccessTokenFetcher : public ProfileOAuth2TokenServiceObserver, const std::string& oauth_consumer_name, ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, TokenCallback callback, Mode mode); @@ -237,7 +237,7 @@ class AccessTokenFetcher : public ProfileOAuth2TokenServiceObserver, const std::string client_secret_; ProfileOAuth2TokenService* token_service_; scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; - const identity::ScopeSet scopes_; + const ScopeSet scopes_; const Mode mode_; // NOTE: This callback should only be invoked from |RunCallbackAndMaybeDie|, diff --git a/chromium/components/signin/public/identity_manager/account_info.cc b/chromium/components/signin/public/identity_manager/account_info.cc index 17c2856cf73..e9aef3c5f55 100644 --- a/chromium/components/signin/public/identity_manager/account_info.cc +++ b/chromium/components/signin/public/identity_manager/account_info.cc @@ -7,8 +7,8 @@ #if defined(OS_ANDROID) #include "base/android/jni_string.h" -#include "components/signin/internal/identity_manager/android/jni_headers/CoreAccountId_jni.h" -#include "components/signin/internal/identity_manager/android/jni_headers/CoreAccountInfo_jni.h" +#include "components/signin/public/android/jni_headers/CoreAccountId_jni.h" +#include "components/signin/public/android/jni_headers/CoreAccountInfo_jni.h" #endif namespace { @@ -153,7 +153,7 @@ CoreAccountInfo ConvertFromJavaCoreAccountInfo( account.gaia = base::android::ConvertJavaStringToUTF8( signin::Java_CoreAccountInfo_getGaiaId(env, j_core_account_info)); account.email = base::android::ConvertJavaStringToUTF8( - signin::Java_CoreAccountInfo_getName(env, j_core_account_info)); + signin::Java_CoreAccountInfo_getEmail(env, j_core_account_info)); return account; } diff --git a/chromium/components/signin/public/identity_manager/account_info.h b/chromium/components/signin/public/identity_manager/account_info.h index 0c7f93def8e..93f6d86d311 100644 --- a/chromium/components/signin/public/identity_manager/account_info.h +++ b/chromium/components/signin/public/identity_manager/account_info.h @@ -61,6 +61,7 @@ struct AccountInfo : public CoreAccountInfo { std::string hosted_domain; std::string locale; std::string picture_url; + std::string last_downloaded_image_url_with_size; gfx::Image account_image; bool is_child_account = false; diff --git a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h index b60720e00b8..d29441121f8 100644 --- a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h @@ -15,6 +15,12 @@ struct CoreAccountId; class GoogleServiceAuthError; +namespace network { +namespace mojom { +class CookieManager; +} +} // namespace network + namespace signin { struct MultiloginParameters; @@ -24,12 +30,33 @@ enum class SetAccountsInCookieResult; // accounts into the cookie jar tracking the list of logged-in Google sessions. class AccountsCookieMutator { public: + // Delegate class used to interact with storage partitions other than the + // default one. The default storage partition is managed by the SigninClient. + class PartitionDelegate { + public: + // Creates a new GaiaAuthFetcher for the partition. + virtual std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition( + GaiaAuthConsumer* consumer) = 0; + + // Returns the CookieManager for the partition. + virtual network::mojom::CookieManager* GetCookieManagerForPartition() = 0; + }; + + // Task handle for SetAccountsInCookieForPartition. Deleting this object + // cancels the task. Must not outlive the AccountsInCookieMutator. + class SetAccountsInCookieTask { + public: + virtual ~SetAccountsInCookieTask() = default; + }; + AccountsCookieMutator() = default; virtual ~AccountsCookieMutator() = default; typedef base::OnceCallback<void(const CoreAccountId& account_id, const GoogleServiceAuthError& error)> AddAccountToCookieCompletedCallback; + typedef base::OnceCallback<void(const GoogleServiceAuthError& error)> + LogOutFromCookieCompletedCallback; // Adds an account identified by |account_id| to the cookie responsible for // tracking the list of logged-in Google sessions across the web. @@ -67,6 +94,20 @@ class AccountsCookieMutator { base::OnceCallback<void(SetAccountsInCookieResult)> set_accounts_in_cookies_completed_callback) = 0; + // This is similar to SetAccountsInCookie, but allow specifying the partition + // where the cookies are set. This function must not be used with the default + // partition (use SetAccountsInCookie instead). + // + // The returned SetAccountsInCookieTask must not outlive the + // AccountsCookieMutator. If the task is deleted, all network requests are + // cancelled; the partition delegate and the callback will not be called. + virtual std::unique_ptr<SetAccountsInCookieTask> + SetAccountsInCookieForPartition( + PartitionDelegate* partition_delegate, + const MultiloginParameters& parameters, + base::OnceCallback<void(SetAccountsInCookieResult)> + set_accounts_in_cookies_completed_callback) = 0; + // Triggers a ListAccounts fetch. Can be used in circumstances where clients // know that the contents of the Gaia cookie might have changed. virtual void TriggerCookieJarUpdate() = 0; @@ -82,7 +123,9 @@ class AccountsCookieMutator { #endif // Remove all accounts from the Gaia cookie. - virtual void LogOutAllAccounts(gaia::GaiaSource source) = 0; + virtual void LogOutAllAccounts( + gaia::GaiaSource source, + LogOutFromCookieCompletedCallback completion_callback) = 0; private: DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutator); diff --git a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc index 42dc77d50d0..f12dcec9c08 100644 --- a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc @@ -26,6 +26,7 @@ #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" +#include "services/network/test/test_cookie_manager.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -54,12 +55,15 @@ enum class AccountsCookiesMutatorAction { kTriggerCookieJarUpdateNoAccounts, kTriggerCookieJarUpdateOneAccount, kTriggerOnCookieChangeNoAccounts, + kLogOutFromCookie, }; } // namespace namespace signin { -class AccountsCookieMutatorTest : public testing::Test { +class AccountsCookieMutatorTest + : public testing::Test, + public AccountsCookieMutator::PartitionDelegate { public: const CoreAccountId kTestUnavailableAccountId; const CoreAccountId kTestOtherUnavailableAccountId; @@ -128,6 +132,13 @@ class AccountsCookieMutatorTest : public testing::Test { case AccountsCookiesMutatorAction::kTriggerOnCookieChangeNoAccounts: SetListAccountsResponseNoAccounts(GetTestURLLoaderFactory()); break; + case AccountsCookiesMutatorAction::kLogOutFromCookie: + GetTestURLLoaderFactory()->AddResponse( + GaiaUrls::GetInstance() + ->LogOutURLWithSource(GaiaConstants::kChromeSource) + .spec(), + std::string(), net::HTTP_OK); + break; } } @@ -146,10 +157,22 @@ class AccountsCookieMutatorTest : public testing::Test { } private: + // AccountsCookieMutator::PartitionDelegate + std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition( + GaiaAuthConsumer* consumer) override { + return test_signin_client_.CreateGaiaAuthFetcher(consumer, + gaia::GaiaSource::kChrome); + } + + network::mojom::CookieManager* GetCookieManagerForPartition() override { + return &cookie_manager_for_partition_; + } + base::test::TaskEnvironment task_environment_; sync_preferences::TestingPrefServiceSyncable prefs_; TestSigninClient test_signin_client_; IdentityTestEnvironment identity_test_env_; + network::TestCookieManager cookie_manager_for_partition_; DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorTest); }; @@ -360,6 +383,64 @@ TEST_F(AccountsCookieMutatorTest, SetAccountsInCookie_AllExistingAccounts) { run_loop.Run(); } +// Test that trying to set a list of accounts in a partitionned cookie jar where +// all of those accounts have refresh tokens in IdentityManager results in them +// being successfully set. +TEST_F(AccountsCookieMutatorTest, + SetAccountsInCookieForPartition_AllExistingAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kSetAccountsInCookie); + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + CoreAccountId account_id = AddAcountWithRefreshToken(kTestAccountEmail); + CoreAccountId other_account_id = + AddAcountWithRefreshToken(kTestOtherAccountEmail); + base::RunLoop run_loop; + MultiloginParameters parameters = { + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id, other_account_id}}; + std::unique_ptr<AccountsCookieMutator::SetAccountsInCookieTask> task = + accounts_cookie_mutator()->SetAccountsInCookieForPartition( + this, parameters, + base::BindOnce( + [](base::OnceClosure quit_closure, + SetAccountsInCookieResult result) { + EXPECT_EQ(result, SetAccountsInCookieResult::kSuccess); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); + + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + account_id, kTestAccessToken, + base::Time::Now() + base::TimeDelta::FromHours(1)); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + other_account_id, kTestAccessToken, + base::Time::Now() + base::TimeDelta::FromHours(1)); + + run_loop.Run(); +} + +// Test that setting accounts in a partition can be cancelled. +TEST_F(AccountsCookieMutatorTest, SetAccountsInCookieForPartition_Cancel) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kSetAccountsInCookie); + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + CoreAccountId account_id = AddAcountWithRefreshToken(kTestAccountEmail); + CoreAccountId other_account_id = + AddAcountWithRefreshToken(kTestOtherAccountEmail); + MultiloginParameters parameters = { + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id, other_account_id}}; + std::unique_ptr<AccountsCookieMutator::SetAccountsInCookieTask> task = + accounts_cookie_mutator()->SetAccountsInCookieForPartition( + this, parameters, + base::BindOnce([](SetAccountsInCookieResult) { NOTREACHED(); })); + task.reset(); +} + // Test triggering the update of a cookie jar with no accounts works. TEST_F(AccountsCookieMutatorTest, TriggerCookieJarUpdate_NoListedAccounts) { PrepareURLLoaderResponsesForAction( @@ -432,19 +513,19 @@ TEST_F(AccountsCookieMutatorTest, ForceTriggerOnCookieChange) { // Test that trying to log out all sessions generates the right network request. TEST_F(AccountsCookieMutatorTest, LogOutAllAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kLogOutFromCookie); + base::RunLoop run_loop; - GetTestURLLoaderFactory()->SetInterceptor(base::BindRepeating( - [](base::OnceClosure quit_closure, - const network::ResourceRequest& request) { - EXPECT_EQ(request.url.spec(), - GaiaUrls::GetInstance() - ->LogOutURLWithSource(GaiaConstants::kChromeSource) - .spec()); - std::move(quit_closure).Run(); - }, - run_loop.QuitClosure())); - - accounts_cookie_mutator()->LogOutAllAccounts(gaia::GaiaSource::kChrome); + accounts_cookie_mutator()->LogOutAllAccounts( + gaia::GaiaSource::kChrome, base::BindOnce( + [](base::OnceClosure quit_closure, + const GoogleServiceAuthError& error) { + EXPECT_EQ(error.state(), + GoogleServiceAuthError::NONE); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); run_loop.Run(); } diff --git a/chromium/components/signin/public/identity_manager/android/BUILD.gn b/chromium/components/signin/public/identity_manager/android/BUILD.gn deleted file mode 100644 index 48e7d17cf78..00000000000 --- a/chromium/components/signin/public/identity_manager/android/BUILD.gn +++ /dev/null @@ -1,45 +0,0 @@ -import("//build/config/android/rules.gni") - -android_library("java") { - deps = [ - "//base:base_java", - "//base:jni_java", - "//components/signin/core/browser/android:java", - "//net/android:net_java", - "//third_party/android_deps:android_support_v4_java", - "//third_party/android_deps:androidx_annotation_annotation_java", - ] - - srcjar_deps = [ - "//components/signin/public/base:signin_metrics_enum_javagen", - "//components/signin/public/identity_manager:identity_manager_enum_javagen", - ] - - java_files = [ - "java/src/org/chromium/components/signin/identitymanager/CoreAccountId.java", - "java/src/org/chromium/components/signin/identitymanager/CoreAccountInfo.java", - "java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", - "java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", - "java/src/org/chromium/components/signin/identitymanager/OAuth2TokenService.java", - ] - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] -} - -android_library("javatests") { - testonly = true - deps = [ - ":java", - "//base:base_java", - "//base:base_java_test_support", - "//components/signin/core/browser/android:java", - "//components/signin/core/browser/android:signin_java_test_support", - "//third_party/android_deps:com_android_support_support_annotations_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", - "//third_party/jsr-305:jsr_305_javalib", - "//third_party/junit", - ] - - java_files = [ "javatests/src/org/chromium/components/signin/identitymanager/OAuth2TokenServiceTest.java" ] -} diff --git a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountInfo.java b/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountInfo.java deleted file mode 100644 index d72e0b52aaa..00000000000 --- a/chromium/components/signin/public/identity_manager/android/java/src/org/chromium/components/signin/identitymanager/CoreAccountInfo.java +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.components.signin.identitymanager; - -import android.accounts.Account; - -import androidx.annotation.NonNull; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.components.signin.AccountManagerFacade; - -/** - * Structure storing the core information about a Google account that is always known. The {@link - * CoreAccountInfo} for a given user is almost always the same but it may change in some rare cases. - * For example, the {@link android.accounts.Account} will change if a user changes email. - * - * This class has a native counterpart called CoreAccountInfo. There are several differences between - * these two classes: - * - Android class additionally exposes {@link android.accounts.Account} object for interactions - * with the system. - * - Android class has the "account name" whereas the native class has "email". This is the same - * string, only the naming in different. - */ -public class CoreAccountInfo { - private final CoreAccountId mId; - private final Account mAccount; - private final String mGaiaId; - - /** - * Constructs a CoreAccountInfo with the provided parameters - * @param id A CoreAccountId associated with the account, equal to either account.name or - * gaiaId. - * @param account Android account. - * @param gaiaId String representation of the Gaia ID. Must not be an email address. - */ - public CoreAccountInfo( - @NonNull CoreAccountId id, @NonNull Account account, @NonNull String gaiaId) { - assert id != null; - assert account != null; - assert gaiaId != null; - assert !gaiaId.contains("@"); - - mId = id; - mAccount = account; - mGaiaId = gaiaId; - } - - @CalledByNative - private CoreAccountInfo( - @NonNull CoreAccountId id, @NonNull String name, @NonNull String gaiaId) { - assert id != null; - assert name != null; - assert gaiaId != null; - assert !gaiaId.contains("@"); - - mId = id; - mAccount = AccountManagerFacade.createAccountFromName(name); - mGaiaId = gaiaId; - } - - /** - * Returns a unique identifier of the current account. - */ - @CalledByNative - public CoreAccountId getId() { - return mId; - } - - /** - * Returns a name of the current account. - */ - @CalledByNative - public String getName() { - return mAccount.name; - } - - /** - * Returns the string representation of the Gaia ID - */ - @CalledByNative - public String getGaiaId() { - return mGaiaId; - } - - /** - * Returns {@link android.accounts.Account} object holding a name of the current account. - */ - public Account getAccount() { - return mAccount; - } - - @Override - public String toString() { - return String.format("CoreAccountInfo{id[%s], name[%s]}", getId(), getName()); - } - - @Override - public int hashCode() { - return 31 * mId.hashCode() + mAccount.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CoreAccountInfo)) return false; - CoreAccountInfo other = (CoreAccountInfo) obj; - return mId.equals(other.mId) && mAccount.equals(other.mAccount); - } -} diff --git a/chromium/components/signin/public/identity_manager/consent_level.h b/chromium/components/signin/public/identity_manager/consent_level.h new file mode 100644 index 00000000000..fc452fe1ce5 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/consent_level.h @@ -0,0 +1,26 @@ +// Copyright 2020 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_PUBLIC_IDENTITY_MANAGER_CONSENT_LEVEL_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_CONSENT_LEVEL_H_ + +namespace signin { + +// ConsentLevel is the required level of user consent for an identity operation +// (for example to fetch an OAuth2 access token). +enum class ConsentLevel { + // No specific consent required. In particular, browser sync consent is not + // required. Operations are allowed if the user is signed in to Chrome. See + // "unconsented primary account" in ./README.md. + kNotRequired, + + // Chrome browser sync consent is required. Historically (before DICE and + // Project Butter) most operations implicitly required this consent. See + // "primary account" in ./README.md. + kSync +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_CONSENT_LEVEL_H_ diff --git a/chromium/components/signin/public/identity_manager/identity_manager.cc b/chromium/components/signin/public/identity_manager/identity_manager.cc index 2f68233f856..47d00212a9a 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager.cc @@ -23,8 +23,8 @@ #if defined(OS_ANDROID) #include "base/android/jni_string.h" -#include "components/signin/internal/identity_manager/android/jni_headers/IdentityManager_jni.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "components/signin/public/android/jni_headers/IdentityManager_jni.h" #endif #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -111,35 +111,30 @@ void IdentityManager::RemoveObserver(Observer* observer) { } // TODO(862619) change return type to base::Optional<CoreAccountInfo> -CoreAccountInfo IdentityManager::GetPrimaryAccountInfo() const { +CoreAccountInfo IdentityManager::GetPrimaryAccountInfo( + ConsentLevel consent) const { + if (consent == ConsentLevel::kNotRequired) { + return primary_account_manager_->GetUnconsentedPrimaryAccountInfo(); + } return primary_account_manager_->GetAuthenticatedAccountInfo(); } -CoreAccountId IdentityManager::GetPrimaryAccountId() const { - return GetPrimaryAccountInfo().account_id; +CoreAccountId IdentityManager::GetPrimaryAccountId(ConsentLevel consent) const { + return GetPrimaryAccountInfo(consent).account_id; } -bool IdentityManager::HasPrimaryAccount() const { +bool IdentityManager::HasPrimaryAccount(ConsentLevel consent) const { + if (consent == ConsentLevel::kNotRequired) { + return primary_account_manager_->HasUnconsentedPrimaryAccount(); + } return primary_account_manager_->IsAuthenticated(); } -CoreAccountId IdentityManager::GetUnconsentedPrimaryAccountId() const { - return GetUnconsentedPrimaryAccountInfo().account_id; -} - -CoreAccountInfo IdentityManager::GetUnconsentedPrimaryAccountInfo() const { - return primary_account_manager_->GetUnconsentedPrimaryAccountInfo(); -} - -bool IdentityManager::HasUnconsentedPrimaryAccount() const { - return primary_account_manager_->HasUnconsentedPrimaryAccount(); -} - std::unique_ptr<AccessTokenFetcher> IdentityManager::CreateAccessTokenFetcherForAccount( const CoreAccountId& account_id, const std::string& oauth_consumer_name, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) { return std::make_unique<AccessTokenFetcher>(account_id, oauth_consumer_name, @@ -152,7 +147,7 @@ IdentityManager::CreateAccessTokenFetcherForAccount( const CoreAccountId& account_id, const std::string& oauth_consumer_name, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) { return std::make_unique<AccessTokenFetcher>( @@ -166,7 +161,7 @@ IdentityManager::CreateAccessTokenFetcherForClient( const std::string& client_id, const std::string& client_secret, const std::string& oauth_consumer_name, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) { return std::make_unique<AccessTokenFetcher>( @@ -176,7 +171,7 @@ IdentityManager::CreateAccessTokenFetcherForClient( void IdentityManager::RemoveAccessTokenFromCache( const CoreAccountId& account_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, const std::string& access_token) { token_service_->InvalidateAccessToken(account_id, scopes, access_token); } @@ -400,10 +395,6 @@ void IdentityManager::ForceRefreshOfExtendedAccountInfo( account_fetcher_service_->ForceRefreshOfAccountInfo(account_id); } -bool IdentityManager::HasPrimaryAccount(JNIEnv* env) const { - return HasPrimaryAccount(); -} - base::android::ScopedJavaLocalRef<jobject> IdentityManager::GetPrimaryAccountInfo(JNIEnv* env) const { if (HasPrimaryAccount()) @@ -411,13 +402,6 @@ IdentityManager::GetPrimaryAccountInfo(JNIEnv* env) const { return nullptr; } -base::android::ScopedJavaLocalRef<jobject> IdentityManager::GetPrimaryAccountId( - JNIEnv* env) const { - if (HasPrimaryAccount()) - return ConvertToJavaCoreAccountId(env, GetPrimaryAccountId()); - return nullptr; -} - base::android::ScopedJavaLocalRef<jobject> IdentityManager:: FindExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( JNIEnv* env, @@ -436,8 +420,7 @@ IdentityManager::GetAccountsWithRefreshTokens(JNIEnv* env) const { base::android::ScopedJavaLocalRef<jclass> coreaccountinfo_clazz = base::android::GetClass( - env, - "org/chromium/components/signin/identitymanager/CoreAccountInfo"); + env, "org/chromium/components/signin/base/CoreAccountInfo"); base::android::ScopedJavaLocalRef<jobjectArray> array( env, env->NewObjectArray(accounts.size(), coreaccountinfo_clazz.obj(), nullptr)); @@ -501,8 +484,13 @@ IdentityManager::ComputeUnconsentedPrimaryAccountInfo() const { if (HasPrimaryAccount()) return GetPrimaryAccountInfo(); -#if defined(OS_CHROMEOS) || defined(OS_IOS) || defined(OS_ANDROID) - // On ChromeOS and on mobile platforms, we support only the primary account as +#if defined(OS_CHROMEOS) + // Chrome OS directly sets either the primary account or the unconsented + // primary account during login. The user is not allowed to sign out, so + // keep the value set at login (don't reset). + return base::nullopt; +#elif defined(OS_IOS) || defined(OS_ANDROID) + // On iOS and Android platforms, we support only the primary account as // the unconsented primary account. By this early return, we avoid an extra // request to GAIA that lists cookie accounts. return CoreAccountInfo(); @@ -521,12 +509,17 @@ IdentityManager::ComputeUnconsentedPrimaryAccountInfo() const { // If cookies or tokens are not loaded, it is not possible to fully compute // the unconsented primary account. However, if the current unconsented // primary account is no longer valid, it has to be removed. - CoreAccountId current_account = GetUnconsentedPrimaryAccountId(); + CoreAccountId current_account = + GetPrimaryAccountId(ConsentLevel::kNotRequired); if (!current_account.empty()) { if (AreRefreshTokensLoaded() && !HasAccountWithRefreshToken(current_account)) { return CoreAccountInfo(); } + if (!AreRefreshTokensLoaded() && + unconsented_primary_account_revoked_during_load_) { + return CoreAccountInfo(); + } if (cookie_info.accounts_are_fresh && cookie_accounts[0].id != current_account) { return CoreAccountInfo(); @@ -546,7 +539,6 @@ IdentityManager::ComputeUnconsentedPrimaryAccountInfo() const { void IdentityManager::GoogleSigninSucceeded( const CoreAccountInfo& account_info) { - UpdateUnconsentedPrimaryAccount(); for (auto& observer : observer_list_) { observer.OnPrimaryAccountSet(account_info); } @@ -566,10 +558,15 @@ void IdentityManager::UnconsentedPrimaryAccountChanged( observer.OnUnconsentedPrimaryAccountChanged(account_info); } -#if !defined(OS_CHROMEOS) void IdentityManager::GoogleSignedOut(const CoreAccountInfo& account_info) { DCHECK(!HasPrimaryAccount()); DCHECK(!account_info.IsEmpty()); + // This is needed for the case where the user chooses to start syncing + // with an account that is different then the unconsented primary account + // (not the first in cookies) but then cancel. In that case, the tokens stay + // the same. In all the other cases, either the token will be revoked which + // will trigger an update for the unconsented primary account or the + // primary account stays the same but the sync consent is revoked. UpdateUnconsentedPrimaryAccount(); for (auto& observer : observer_list_) { observer.OnPrimaryAccountCleared(account_info); @@ -583,7 +580,6 @@ void IdentityManager::GoogleSignedOut(const CoreAccountInfo& account_info) { } #endif } -#endif // !defined(OS_CHROMEOS) void IdentityManager::OnRefreshTokenAvailable(const CoreAccountId& account_id) { UpdateUnconsentedPrimaryAccount(); @@ -596,6 +592,12 @@ void IdentityManager::OnRefreshTokenAvailable(const CoreAccountId& account_id) { } void IdentityManager::OnRefreshTokenRevoked(const CoreAccountId& account_id) { + if (!AreRefreshTokensLoaded() && + HasPrimaryAccount(ConsentLevel::kNotRequired) && + account_id == GetPrimaryAccountId(ConsentLevel::kNotRequired)) { + unconsented_primary_account_revoked_during_load_ = true; + } + UpdateUnconsentedPrimaryAccount(); for (auto& observer : observer_list_) { observer.OnRefreshTokenRemovedForAccount(account_id); @@ -647,7 +649,7 @@ void IdentityManager::OnGaiaCookieDeletedByUserAction() { void IdentityManager::OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) { + const ScopeSet& scopes) { for (auto& observer : diagnostics_observer_list_) { observer.OnAccessTokenRequested(account_id, consumer_id, scopes); } @@ -656,7 +658,7 @@ void IdentityManager::OnAccessTokenRequested(const CoreAccountId& account_id, void IdentityManager::OnFetchAccessTokenComplete( const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) { for (auto& observer : diagnostics_observer_list_) @@ -665,7 +667,7 @@ void IdentityManager::OnFetchAccessTokenComplete( } void IdentityManager::OnAccessTokenRemoved(const CoreAccountId& account_id, - const identity::ScopeSet& scopes) { + const ScopeSet& scopes) { for (auto& observer : diagnostics_observer_list_) observer.OnAccessTokenRemovedFromCache(account_id, scopes); } @@ -691,7 +693,6 @@ void IdentityManager::OnAccountUpdated(const AccountInfo& info) { const CoreAccountId primary_account_id = GetPrimaryAccountId(); if (primary_account_id == info.account_id) { primary_account_manager_->UpdateAuthenticatedAccountInfo(); - UpdateUnconsentedPrimaryAccount(); } } @@ -701,7 +702,6 @@ void IdentityManager::OnAccountUpdated(const AccountInfo& info) { } void IdentityManager::OnAccountRemoved(const AccountInfo& info) { - UpdateUnconsentedPrimaryAccount(); for (auto& observer : observer_list_) observer.OnExtendedAccountInfoRemoved(info); } diff --git a/chromium/components/signin/public/identity_manager/identity_manager.h b/chromium/components/signin/public/identity_manager/identity_manager.h index f4576e21eb0..b6a365f0195 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager.h +++ b/chromium/components/signin/public/identity_manager/identity_manager.h @@ -18,10 +18,11 @@ #include "components/signin/internal/identity_manager/profile_oauth2_token_service_observer.h" #include "components/signin/public/identity_manager/access_token_fetcher.h" #include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_mutator.h" +#include "components/signin/public/identity_manager/scope_set.h" #include "components/signin/public/identity_manager/ubertoken_fetcher.h" #include "google_apis/gaia/oauth2_access_token_manager.h" -#include "services/identity/public/cpp/scope_set.h" #if defined(OS_ANDROID) #include "base/android/jni_android.h" @@ -161,35 +162,31 @@ class IdentityManager : public KeyedService, void RemoveObserver(Observer* observer); // Provides access to the core information of the user's primary account. + // The primary account may or may not be blessed with the sync consent. // Returns an empty struct if no such info is available, either because there - // is no primary account yet or because the user signed out. - CoreAccountInfo GetPrimaryAccountInfo() const; + // is no primary account yet or because the user signed out or the |consent| + // level required |ConsentLevel::kSync| was not granted. + // Returns a non-empty struct if the primary account exists and was granted + // the required consent level. + // TODO(1046746): Update (./README.md). + CoreAccountInfo GetPrimaryAccountInfo( + ConsentLevel consent = ConsentLevel::kSync) const; // Provides access to the account ID of the user's primary account. Simple // convenience wrapper over GetPrimaryAccountInfo().account_id. - CoreAccountId GetPrimaryAccountId() const; + CoreAccountId GetPrimaryAccountId( + ConsentLevel consent = ConsentLevel::kSync) const; - // Returns whether the user's primary account is available. - bool HasPrimaryAccount() const; - - // Provides access to the core information of the user's unconsented primary - // account (see ./README.md). Returns an empty info, if there is no such - // account. - CoreAccountInfo GetUnconsentedPrimaryAccountInfo() const; - - // Provides access to the account ID of the user's unconsented primary - // account (see ./README.md). Returns an empty id if there is no such account. - CoreAccountId GetUnconsentedPrimaryAccountId() const; - - // Returns whether the user's unconsented primary account (see ./README.md) is - // available. - bool HasUnconsentedPrimaryAccount() const; + // Returns whether the user's primary account is available. If consent is + // |ConsentLevel::kSyn| then true implies that the user has blessed this + // account for sync. + bool HasPrimaryAccount(ConsentLevel consent = ConsentLevel::kSync) const; // Creates an AccessTokenFetcher given the passed-in information. std::unique_ptr<AccessTokenFetcher> CreateAccessTokenFetcherForAccount( const CoreAccountId& account_id, const std::string& oauth_consumer_name, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; @@ -199,7 +196,7 @@ class IdentityManager : public KeyedService, const CoreAccountId& account_id, const std::string& oauth_consumer_name, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; @@ -211,7 +208,7 @@ class IdentityManager : public KeyedService, const std::string& client_id, const std::string& client_secret, const std::string& oauth_consumer_name, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; @@ -220,7 +217,7 @@ class IdentityManager : public KeyedService, // request for |account_id| and |scopes| will fetch a new token from the // network. Otherwise, is a no-op. void RemoveAccessTokenFromCache(const CoreAccountId& account_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, const std::string& access_token); // Provides the information of all accounts that have refresh tokens. @@ -335,20 +332,19 @@ class IdentityManager : public KeyedService, // Called when receiving request for access token. virtual void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) {} + const ScopeSet& scopes) {} // Called when an access token request is completed. Contains diagnostic // information about the access token request. virtual void OnAccessTokenRequestCompleted(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) {} // Called when an access token was removed. - virtual void OnAccessTokenRemovedFromCache( - const CoreAccountId& account_id, - const identity::ScopeSet& scopes) {} + virtual void OnAccessTokenRemovedFromCache(const CoreAccountId& account_id, + const ScopeSet& scopes) {} // Called when a new refresh token is available. Contains diagnostic // information about the source of the operation. @@ -465,6 +461,9 @@ class IdentityManager : public KeyedService, // These test helpers need to use some of the private methods below. friend CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, const std::string& email); + friend CoreAccountInfo SetUnconsentedPrimaryAccount( + IdentityManager* identity_manager, + const std::string& email); friend void SetRefreshTokenForPrimaryAccount( IdentityManager* identity_manager, const std::string& token_value); @@ -496,6 +495,7 @@ class IdentityManager : public KeyedService, AccountInfo account_info); friend void SimulateAccountImageFetch(IdentityManager* identity_manager, const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image); friend void SetFreshnessOfAccountsInGaiaCookie( IdentityManager* identity_manager, @@ -608,9 +608,7 @@ class IdentityManager : public KeyedService, void GoogleSigninSucceeded(const CoreAccountInfo& account_info) override; void UnconsentedPrimaryAccountChanged( const CoreAccountInfo& account_info) override; -#if !defined(OS_CHROMEOS) void GoogleSignedOut(const CoreAccountInfo& account_info) override; -#endif // ProfileOAuth2TokenServiceObserver: void OnRefreshTokenAvailable(const CoreAccountId& account_id) override; @@ -630,14 +628,14 @@ class IdentityManager : public KeyedService, // OAuth2AccessTokenManager::DiagnosticsObserver void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) override; + const ScopeSet& scopes) override; void OnFetchAccessTokenComplete(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) override; void OnAccessTokenRemoved(const CoreAccountId& account_id, - const identity::ScopeSet& scopes) override; + const ScopeSet& scopes) override; // ProfileOAuth2TokenService callbacks: void OnRefreshTokenAvailableFromSource(const CoreAccountId& account_id, @@ -674,6 +672,8 @@ class IdentityManager : public KeyedService, base::ObserverList<DiagnosticsObserver, true>::Unchecked diagnostics_observer_list_; + bool unconsented_primary_account_revoked_during_load_ = false; + #if defined(OS_ANDROID) // Java-side IdentityManager object. base::android::ScopedJavaGlobalRef<jobject> java_identity_manager_; diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc index da113643252..1e95cac5140 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc @@ -147,6 +147,7 @@ std::unique_ptr<IdentityManager> BuildIdentityManager( token_service.get(), primary_account_manager.get()); auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>( + params->signin_client, token_service.get(), gaia_cookie_manager_service.get(), account_tracker_service.get()); auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>( diff --git a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc index a7c1c59673e..95d178947c9 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc @@ -34,9 +34,11 @@ #include "components/signin/public/identity_manager/access_token_info.h" #include "components/signin/public/identity_manager/accounts_cookie_mutator.h" #include "components/signin/public/identity_manager/accounts_mutator.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/device_accounts_synchronizer.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/signin/public/identity_manager/primary_account_mutator.h" +#include "components/signin/public/identity_manager/scope_set.h" #include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "components/signin/public/identity_manager/test_identity_manager_observer.h" #include "components/sync_preferences/testing_pref_service_syncable.h" @@ -99,7 +101,7 @@ class CustomFakeOAuth2AccessTokenManager : public FakeOAuth2AccessTokenManager { // OAuth2AccessTokenManager: void InvalidateAccessTokenImpl(const CoreAccountId& account_id, const std::string& client_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, const std::string& access_token) override { if (on_access_token_invalidated_callback_) { EXPECT_EQ(expected_account_id_to_invalidate_, account_id); @@ -176,22 +178,18 @@ class TestIdentityManagerDiagnosticsObserver const std::string& token_requestor_consumer_id() { return token_requestor_consumer_id_; } - const identity::ScopeSet& token_requestor_scopes() { - return token_requestor_scopes_; - } + const ScopeSet& token_requestor_scopes() { return token_requestor_scopes_; } const CoreAccountId& token_remover_account_id() { return token_remover_account_id_; } - const identity::ScopeSet& token_remover_scopes() { - return token_remover_scopes_; - } + const ScopeSet& token_remover_scopes() { return token_remover_scopes_; } const CoreAccountId& on_access_token_request_completed_account_id() { return access_token_request_completed_account_id_; } const std::string& on_access_token_request_completed_consumer_id() { return access_token_request_completed_consumer_id_; } - const identity::ScopeSet& on_access_token_request_completed_scopes() { + const ScopeSet& on_access_token_request_completed_scopes() { return access_token_request_completed_scopes_; } const GoogleServiceAuthError& on_access_token_request_completed_error() { @@ -202,7 +200,7 @@ class TestIdentityManagerDiagnosticsObserver // IdentityManager::DiagnosticsObserver: void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) override { + const ScopeSet& scopes) override { token_requestor_account_id_ = account_id; token_requestor_consumer_id_ = consumer_id; token_requestor_scopes_ = scopes; @@ -211,16 +209,15 @@ class TestIdentityManagerDiagnosticsObserver std::move(on_access_token_requested_callback_).Run(); } - void OnAccessTokenRemovedFromCache( - const CoreAccountId& account_id, - const identity::ScopeSet& scopes) override { + void OnAccessTokenRemovedFromCache(const CoreAccountId& account_id, + const ScopeSet& scopes) override { token_remover_account_id_ = account_id; token_remover_scopes_ = scopes; } void OnAccessTokenRequestCompleted(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) override { access_token_request_completed_account_id_ = account_id; @@ -238,11 +235,11 @@ class TestIdentityManagerDiagnosticsObserver CoreAccountId token_requestor_account_id_; std::string token_requestor_consumer_id_; CoreAccountId token_remover_account_id_; - identity::ScopeSet token_requestor_scopes_; - identity::ScopeSet token_remover_scopes_; + ScopeSet token_requestor_scopes_; + ScopeSet token_remover_scopes_; CoreAccountId access_token_request_completed_account_id_; std::string access_token_request_completed_consumer_id_; - identity::ScopeSet access_token_request_completed_scopes_; + ScopeSet access_token_request_completed_scopes_; GoogleServiceAuthError access_token_request_completed_error_; }; @@ -356,7 +353,8 @@ class IdentityManagerTest : public testing::Test { } auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>( - gaia_cookie_manager_service.get(), account_tracker_service.get()); + &signin_client_, token_service.get(), gaia_cookie_manager_service.get(), + account_tracker_service.get()); auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>( token_service.get(), gaia_cookie_manager_service.get()); @@ -430,8 +428,8 @@ TEST_F(IdentityManagerTest, PrimaryAccountInfoAtStartup) { EXPECT_EQ(kTestEmail, primary_account_info.email); // Primary account is by definition also unconsented primary account. - EXPECT_EQ(primary_account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(primary_account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); // There is no guarantee that this will be notified via callback on startup. } @@ -460,15 +458,15 @@ TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSignin) { EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); EXPECT_EQ(kTestEmail, primary_account_info.email); - EXPECT_EQ(primary_account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(primary_account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); EXPECT_EQ(primary_account_id, CoreAccountId(kTestGaiaId)); EXPECT_EQ(primary_account_id, primary_account_info.account_id); - EXPECT_EQ(primary_account_id, - identity_manager()->GetUnconsentedPrimaryAccountId()); + EXPECT_EQ(primary_account_id, identity_manager()->GetPrimaryAccountId( + signin::ConsentLevel::kNotRequired)); } // Test that the user signing out results in firing of the IdentityManager @@ -497,14 +495,14 @@ TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSigninAndSignout) { identity_manager()->GetPrimaryAccountInfo(); EXPECT_EQ("", primary_account_info.gaia); EXPECT_EQ("", primary_account_info.email); - EXPECT_EQ(primary_account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(primary_account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); EXPECT_TRUE(primary_account_id.empty()); EXPECT_EQ(primary_account_id, primary_account_info.account_id); - EXPECT_EQ(primary_account_id, - identity_manager()->GetUnconsentedPrimaryAccountId()); + EXPECT_EQ(primary_account_id, identity_manager()->GetPrimaryAccountId( + signin::ConsentLevel::kNotRequired)); } // Test that the primary account's core info remains tracked by the @@ -528,32 +526,35 @@ TEST_F(IdentityManagerTest, EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); EXPECT_EQ(kTestEmail, primary_account_info.email); EXPECT_EQ(CoreAccountId(kTestGaiaId), primary_account_info.account_id); - EXPECT_EQ(primary_account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(primary_account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); EXPECT_EQ(primary_account_id, CoreAccountId(kTestGaiaId)); - EXPECT_EQ(primary_account_id, - identity_manager()->GetUnconsentedPrimaryAccountId()); + EXPECT_EQ(primary_account_id, identity_manager()->GetPrimaryAccountId( + ConsentLevel::kNotRequired)); } #endif // !defined(OS_CHROMEOS) TEST_F(IdentityManagerTest, HasPrimaryAccount) { EXPECT_TRUE(identity_manager()->HasPrimaryAccount()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); // Removing the account from the AccountTrackerService should not cause // IdentityManager to think that there is no longer a primary account. account_tracker()->RemoveAccount(identity_manager()->GetPrimaryAccountId()); EXPECT_TRUE(identity_manager()->HasPrimaryAccount()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); #if !defined(OS_CHROMEOS) // Signing out should cause IdentityManager to recognize that there is no // longer a primary account. ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); EXPECT_FALSE(identity_manager()->HasPrimaryAccount()); - EXPECT_FALSE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); EXPECT_FALSE(identity_manager_observer() ->PrimaryAccountFromClearedCallback() .IsEmpty()); @@ -791,7 +792,9 @@ TEST_F(IdentityManagerTest, // The user still has a primary account. EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo().email, kTestEmail); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo().email, + EXPECT_EQ(identity_manager() + ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired) + .email, kTestEmail); // Add a refresh token for the primary account and check that it @@ -814,7 +817,9 @@ TEST_F(IdentityManagerTest, EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo().email, kTestEmail); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo().email, + EXPECT_EQ(identity_manager() + ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired) + .email, kTestEmail); // Remove the token for the primary account and check that account2 is still @@ -832,7 +837,9 @@ TEST_F(IdentityManagerTest, EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); // The user still has a primary account. EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo().email, kTestEmail); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo().email, + EXPECT_EQ(identity_manager() + ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired) + .email, kTestEmail); } @@ -840,7 +847,8 @@ TEST_F( IdentityManagerTest, HasPrimaryAccountWithRefreshTokenInteractionBetweenPrimaryAndSecondaryAccounts) { EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); // Add a refresh token for a secondary account and check that it doesn't // impact the above state. @@ -850,28 +858,32 @@ TEST_F( SetRefreshTokenForAccount(identity_manager(), account_id2); EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); // Add a refresh token for the primary account and check that it // *does* impact the stsate of HasPrimaryAccountWithRefreshToken(). SetRefreshTokenForPrimaryAccount(identity_manager()); EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); // Remove the token for the secondary account and check that this doesn't flip // the state. RemoveRefreshTokenForAccount(identity_manager(), account_id2); EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); // Remove the token for the primary account and check that this flips the // state. RemoveRefreshTokenForPrimaryAccount(identity_manager()); EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); - EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_TRUE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); } TEST_F( @@ -1364,8 +1376,9 @@ TEST_F(IdentityManagerTest, IdentityManagerReflectsUpdatedEmailAddress) { primary_account_info = identity_manager()->GetPrimaryAccountInfo(); EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); EXPECT_EQ(kTestEmailWithPeriod, primary_account_info.email); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - primary_account_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + primary_account_info); } #endif @@ -1508,8 +1521,9 @@ TEST_F( identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); EXPECT_EQ(kTestEmail2, expected_account_info.email); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - expected_account_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + expected_account_info); EXPECT_EQ( identity_manager_observer()->UnconsentedPrimaryAccountFromCallback(), expected_account_info); @@ -1530,8 +1544,9 @@ TEST_F( // This is still an unconsented primary account, even with invalid refresh // token. - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - expected_account_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + expected_account_info); EXPECT_EQ( identity_manager_observer()->UnconsentedPrimaryAccountFromCallback(), expected_account_info); @@ -1552,11 +1567,14 @@ TEST_F( // With no refresh token, there is no unconsented primary account any more. CoreAccountInfo empty_info; - EXPECT_FALSE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); EXPECT_EQ( identity_manager_observer()->UnconsentedPrimaryAccountFromCallback(), empty_info); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), empty_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + empty_info); } TEST_F(IdentityManagerTest, UnconsentedPrimaryAccountNotChangedOnSignout) { @@ -1570,8 +1588,8 @@ TEST_F(IdentityManagerTest, UnconsentedPrimaryAccountNotChangedOnSignout) { {{account_info.email, account_info.gaia}}); SetRefreshTokenForAccount(identity_manager(), account_info.account_id, "refresh-token"); - EXPECT_EQ(account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); EXPECT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo()); EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); @@ -1594,8 +1612,8 @@ TEST_F(IdentityManagerTest, UnconsentedPrimaryAccountNotChangedOnSignout) { ClearPrimaryAccountPolicy::KEEP_ALL_ACCOUNTS); // Primary account is cleared, but unconsented account is not. EXPECT_FALSE(identity_manager()->HasPrimaryAccount()); - EXPECT_EQ(account_info, - identity_manager()->GetUnconsentedPrimaryAccountInfo()); + EXPECT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo( + ConsentLevel::kNotRequired)); // OnUnconsentedPrimaryAccountChanged was not fired. EXPECT_FALSE(observer.called_); } @@ -1609,8 +1627,9 @@ TEST_F(IdentityManagerTest, identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); EXPECT_EQ(kTestEmail2, account_info.email); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - account_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + account_info); // Make the cookies stale and remove the account. signin::SetFreshnessOfAccountsInGaiaCookie(identity_manager(), false); @@ -1619,8 +1638,9 @@ TEST_F(IdentityManagerTest, identity_manager()->GetAccountsInCookieJar(); ASSERT_FALSE(cookie_info.accounts_are_fresh); // Unconsented account was removed. - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - CoreAccountInfo()); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + CoreAccountInfo()); } TEST_F(IdentityManagerTest, @@ -1637,8 +1657,9 @@ TEST_F(IdentityManagerTest, {{main_account_info.email, main_account_info.gaia}, {secondary_account_info.email, secondary_account_info.gaia}}); - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - main_account_info); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + main_account_info); // Make the cookies stale and remove the main account. signin::SetFreshnessOfAccountsInGaiaCookie(identity_manager(), false); @@ -1648,8 +1669,46 @@ TEST_F(IdentityManagerTest, identity_manager()->GetAccountsInCookieJar(); ASSERT_FALSE(cookie_info.accounts_are_fresh); // Unconsented account was removed. - EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), - CoreAccountInfo()); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + CoreAccountInfo()); +} + +TEST_F(IdentityManagerTest, UnconsentedPrimaryAccountDuringLoad) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + // Add two accounts with cookies. + AccountInfo main_account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + AccountInfo secondary_account_info = + MakeAccountAvailable(identity_manager(), kTestEmail3); + SetCookieAccounts( + identity_manager(), test_url_loader_factory(), + {{main_account_info.email, main_account_info.gaia}, + {secondary_account_info.email, secondary_account_info.gaia}}); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + main_account_info); + + // Set the token service in "loading" mode. + token_service()->set_all_credentials_loaded_for_testing(false); + + // Unconsented primary account is available while tokens are not loaded. + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + main_account_info); + // Revoking an unrelated token doesn't change the unconsented primary account. + token_service()->RevokeCredentials(secondary_account_info.account_id); + EXPECT_EQ( + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired), + main_account_info); + // Revoke the unconsented primary account. + token_service()->RevokeCredentials(main_account_info.account_id); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); + // Finish the token load. + token_service()->set_all_credentials_loaded_for_testing(true); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); } #endif diff --git a/chromium/components/signin/public/identity_manager/identity_mutator.cc b/chromium/components/signin/public/identity_manager/identity_mutator.cc index 66c235db0ae..aa53dd1c432 100644 --- a/chromium/components/signin/public/identity_manager/identity_mutator.cc +++ b/chromium/components/signin/public/identity_manager/identity_mutator.cc @@ -11,7 +11,7 @@ #if defined(OS_ANDROID) #include "base/android/jni_string.h" -#include "components/signin/internal/identity_manager/android/jni_headers/IdentityMutator_jni.h" +#include "components/signin/public/android/jni_headers/IdentityMutator_jni.h" #include "components/signin/public/identity_manager/account_info.h" #endif diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.cc b/chromium/components/signin/public/identity_manager/identity_test_environment.cc index 088157de9d8..8c6065b6c06 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_environment.cc +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.cc @@ -25,6 +25,7 @@ #include "components/signin/internal/identity_manager/primary_account_policy_manager_impl.h" #include "components/signin/public/base/test_signin_client.h" #include "components/signin/public/identity_manager/accounts_mutator.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/device_accounts_synchronizer.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" @@ -209,7 +210,8 @@ IdentityTestEnvironment::BuildIdentityManagerForTests( token_service.get(), gaia_cookie_manager_service.get()); auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>( - gaia_cookie_manager_service.get(), account_tracker_service.get()); + signin_client, token_service.get(), gaia_cookie_manager_service.get(), + account_tracker_service.get()); std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer; #if defined(OS_IOS) @@ -251,6 +253,11 @@ CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount( return signin::SetPrimaryAccount(identity_manager(), email); } +CoreAccountInfo IdentityTestEnvironment::SetUnconsentedPrimaryAccount( + const std::string& email) { + return signin::SetUnconsentedPrimaryAccount(identity_manager(), email); +} + void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() { signin::SetRefreshTokenForPrimaryAccount(identity_manager()); } @@ -268,6 +275,31 @@ AccountInfo IdentityTestEnvironment::MakePrimaryAccountAvailable( return signin::MakePrimaryAccountAvailable(identity_manager(), email); } +AccountInfo IdentityTestEnvironment::MakeUnconsentedPrimaryAccountAvailable( + const std::string& email) { + DCHECK(!identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); +#if defined(OS_CHROMEOS) + // Chrome OS sets the unconsented primary account during login and does not + // allow signout. + AccountInfo account_info = MakeAccountAvailable(email); + identity_manager()->GetPrimaryAccountMutator()->SetUnconsentedPrimaryAccount( + account_info.account_id); +#elif defined(OS_IOS) || defined(OS_ANDROID) + // iOS and Android only support the primary account. + AccountInfo account_info = MakePrimaryAccountAvailable(email); +#else + // Desktop platforms. + AccountInfo account_info = + MakeAccountAvailableWithCookies(email, GetTestGaiaIdForEmail(email)); + base::RunLoop().RunUntilIdle(); +#endif + DCHECK(identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired)); + DCHECK_EQ(email, identity_manager() + ->GetPrimaryAccountInfo(ConsentLevel::kNotRequired) + .email); + return account_info; +} + void IdentityTestEnvironment::ClearPrimaryAccount( ClearPrimaryAccountPolicy policy) { signin::ClearPrimaryAccount(identity_manager(), policy); @@ -349,7 +381,7 @@ void IdentityTestEnvironment:: const std::string& token, const base::Time& expiration, const std::string& id_token, - const identity::ScopeSet& scopes) { + const ScopeSet& scopes) { WaitForAccessTokenRequestIfNecessary(base::nullopt); fake_token_service()->IssueTokenForScope( scopes, @@ -390,7 +422,7 @@ IdentityTestEnvironment::AccessTokenRequestState::operator=( void IdentityTestEnvironment::OnAccessTokenRequested( const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) { + const ScopeSet& scopes) { // Post a task to handle this access token request in order to support the // case where the access token request is handled synchronously in the // production code, in which case this callback could be coming in ahead diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.h b/chromium/components/signin/public/identity_manager/identity_test_environment.h index 5c8773399f1..e6487ed8341 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_environment.h +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.h @@ -14,6 +14,7 @@ #include "components/signin/public/base/signin_client.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" +#include "components/signin/public/identity_manager/scope_set.h" class FakeProfileOAuth2TokenService; class IdentityTestEnvironmentProfileAdaptor; @@ -90,6 +91,10 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { // CoreAccountInfo of the newly-set account. CoreAccountInfo SetPrimaryAccount(const std::string& email); + // As above, but adds an "unconsented" primary account. See ./README.md for + // the distinction between primary and unconsented primary accounts. + CoreAccountInfo SetUnconsentedPrimaryAccount(const std::string& email); + // Sets a refresh token for the primary account (which must already be set). // Before updating the refresh token, blocks until refresh tokens are loaded. // After updating the token, blocks until the update is processed by @@ -104,6 +109,8 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { // Removes any refresh token for the primary account, if present. Blocks until // the refresh token is removed. + // NOTE: Call EnableRemovalOfExtendedAccountInfo() before this if the test + // expects IdentityManager::Observer::OnExtendedAccountInfoRemoved() to fire. void RemoveRefreshTokenForPrimaryAccount(); // Makes the primary account available for the given email address, generating @@ -114,6 +121,11 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { // Returns the AccountInfo of the newly-available account. AccountInfo MakePrimaryAccountAvailable(const std::string& email); + // Like MakeAccountAvailable(), but adds an "unconsented" primary account. See + // ./README.md for the distinction between primary account and unconsented + // primary account. + AccountInfo MakeUnconsentedPrimaryAccountAvailable(const std::string& email); + // Combination of MakeAccountAvailable() and SetCookieAccounts() for a single // account. It makes an account available for the given email address, and // GAIA ID, setting the cookies and the refresh token that correspond uniquely @@ -152,6 +164,8 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { // Removes any refresh token that is present for the given account. Blocks // until the refresh token is removed. // NOTE: See disclaimer at top of file re: direct usage. + // NOTE: Call EnableRemovalOfExtendedAccountInfo() before this if the test + // expects IdentityManager::Observer::OnExtendedAccountInfoRemoved() to fire. void RemoveRefreshTokenForAccount(const CoreAccountId& account_id); // Updates the persistent auth error set on |account_id| which must be a known @@ -211,7 +225,7 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { const std::string& token, const base::Time& expiration, const std::string& id_token, - const identity::ScopeSet& scopes); + const ScopeSet& scopes); // Issues |error| in response to any access token request that either has (a) // already occurred and has not been matched by a previous call to this or @@ -321,7 +335,7 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { // IdentityManager::DiagnosticsObserver: void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, - const identity::ScopeSet& scopes) override; + const ScopeSet& scopes) override; // Handles the notification that an access token request was received for // |account_id|. Invokes |on_access_token_request_callback_| if the latter diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.cc b/chromium/components/signin/public/identity_manager/identity_test_utils.cc index 10c95d01d0c..6d6cc547450 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_utils.cc +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.cc @@ -14,13 +14,14 @@ #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" #include "components/signin/public/base/list_accounts_test_utils.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/test_identity_manager_observer.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_constants.h" #if defined(OS_ANDROID) -#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h" #endif namespace signin { @@ -74,6 +75,21 @@ void UpdateRefreshTokenForAccount( run_loop.Run(); } +// Ensures that an account for |email| exists in the AccountTrackerService, +// seeding it if necessary. Returns AccountInfo for the account. +AccountInfo EnsureAccountExists(AccountTrackerService* account_tracker_service, + const std::string& email) { + AccountInfo account_info = + account_tracker_service->FindAccountInfoByEmail(email); + if (account_info.account_id.empty()) { + std::string gaia_id = GetTestGaiaIdForEmail(email); + account_tracker_service->SeedAccountInfo(gaia_id, email); + account_info = account_tracker_service->FindAccountInfoByEmail(email); + DCHECK(!account_info.account_id.empty()); + } + return account_info; +} + } // namespace CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, @@ -83,18 +99,9 @@ CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, identity_manager->GetPrimaryAccountManager(); DCHECK(!primary_account_manager->IsAuthenticated()); - AccountTrackerService* account_tracker_service = - identity_manager->GetAccountTrackerService(); AccountInfo account_info = - account_tracker_service->FindAccountInfoByEmail(email); - if (account_info.account_id.empty()) { - std::string gaia_id = GetTestGaiaIdForEmail(email); - account_tracker_service->SeedAccountInfo(gaia_id, email); - account_info = account_tracker_service->FindAccountInfoByEmail(email); - } - - std::string gaia_id = account_info.gaia; - DCHECK(!gaia_id.empty()); + EnsureAccountExists(identity_manager->GetAccountTrackerService(), email); + DCHECK(!account_info.gaia.empty()); primary_account_manager->SignIn(email); @@ -103,6 +110,27 @@ CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, return identity_manager->GetPrimaryAccountInfo(); } +CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager, + const std::string& email) { + DCHECK(!identity_manager->HasPrimaryAccount(ConsentLevel::kNotRequired)); + + AccountInfo account_info = + EnsureAccountExists(identity_manager->GetAccountTrackerService(), email); + DCHECK(!account_info.gaia.empty()); + + PrimaryAccountManager* primary_account_manager = + identity_manager->GetPrimaryAccountManager(); + primary_account_manager->SetUnconsentedPrimaryAccountInfo(account_info); + + DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kNotRequired)); + DCHECK_EQ(account_info.gaia, + identity_manager + ->GetPrimaryAccountInfo(signin::ConsentLevel::kNotRequired) + .gaia); + return identity_manager->GetPrimaryAccountInfo( + signin::ConsentLevel::kNotRequired); +} + void SetRefreshTokenForPrimaryAccount(IdentityManager* identity_manager, const std::string& token_value) { DCHECK(identity_manager->HasPrimaryAccount()); @@ -313,10 +341,12 @@ void UpdateAccountInfoForAccount(IdentityManager* identity_manager, void SimulateAccountImageFetch(IdentityManager* identity_manager, const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image) { AccountTrackerService* account_tracker_service = identity_manager->GetAccountTrackerService(); - account_tracker_service->SetAccountImage(account_id, image); + account_tracker_service->SetAccountImage(account_id, image_url_with_size, + image); } void SetFreshnessOfAccountsInGaiaCookie(IdentityManager* identity_manager, @@ -351,7 +381,7 @@ void DisableAccessTokenFetchRetries(IdentityManager* identity_manager) { #if defined(OS_ANDROID) void DisableInteractionWithSystemAccounts() { - OAuth2TokenServiceDelegateAndroid:: + ProfileOAuth2TokenServiceDelegateAndroid:: set_disable_interaction_with_system_accounts(); } #endif diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.h b/chromium/components/signin/public/identity_manager/identity_test_utils.h index 710e2e478ee..46d26a5104b 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_utils.h +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.h @@ -53,6 +53,11 @@ class IdentityManager; CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, const std::string& email); +// As above, but adds an "unconsented" primary account. See ./README.md for +// the distinction between primary and unconsented primary accounts. +CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager, + const std::string& email); + // Sets a refresh token for the primary account (which must already be set). // Blocks until the refresh token is set. If |token_value| is empty a default // value will be used instead. @@ -148,6 +153,7 @@ void UpdateAccountInfoForAccount(IdentityManager* identity_manager, void SimulateAccountImageFetch(IdentityManager* identity_manager, const CoreAccountId& account_id, + const std::string& image_url_with_size, const gfx::Image& image); // Sets whether the list of accounts in Gaia cookie jar is fresh and does not diff --git a/chromium/components/signin/public/identity_manager/identity_utils.cc b/chromium/components/signin/public/identity_manager/identity_utils.cc index 11061a1d694..ec9f6309b72 100644 --- a/chromium/components/signin/public/identity_manager/identity_utils.cc +++ b/chromium/components/signin/public/identity_manager/identity_utils.cc @@ -6,6 +6,7 @@ #include <string> +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/prefs/pref_service.h" diff --git a/chromium/components/signin/public/identity_manager/identity_utils.h b/chromium/components/signin/public/identity_manager/identity_utils.h index 62927ab4e7e..2ca8a7ec29a 100644 --- a/chromium/components/signin/public/identity_manager/identity_utils.h +++ b/chromium/components/signin/public/identity_manager/identity_utils.h @@ -1,18 +1,11 @@ // Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// -// Functions that are shared between the Identity Service implementation and its -// consumers. Currently in //components/signin because they are used by classes -// in this component, which cannot depend on //services/identity to avoid a -// dependency cycle. When these classes have no direct consumers and are moved -// to //services/identity, these functions should correspondingly be moved to -// //services/identity/public/cpp. #ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_UTILS_H_ #define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_UTILS_H_ -#include "base/strings/string_piece.h" +#include <string> class PrefService; diff --git a/chromium/components/signin/public/identity_manager/ios/BUILD.gn b/chromium/components/signin/public/identity_manager/ios/BUILD.gn index 0f06fb4a39b..7ab9b688998 100644 --- a/chromium/components/signin/public/identity_manager/ios/BUILD.gn +++ b/chromium/components/signin/public/identity_manager/ios/BUILD.gn @@ -10,9 +10,7 @@ source_set("ios") { "device_accounts_provider.mm", ] - public_deps = [ - "//base", - ] + public_deps = [ "//base" ] } source_set("test_support") { diff --git a/chromium/components/signin/public/identity_manager/objc/BUILD.gn b/chromium/components/signin/public/identity_manager/objc/BUILD.gn index ba323fe7dae..950157c0990 100644 --- a/chromium/components/signin/public/identity_manager/objc/BUILD.gn +++ b/chromium/components/signin/public/identity_manager/objc/BUILD.gn @@ -9,7 +9,5 @@ source_set("objc") { "identity_manager_observer_bridge.mm", ] - public_deps = [ - "//components/signin/public/identity_manager", - ] + public_deps = [ "//components/signin/public/identity_manager" ] } diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc index 4b0ce696467..56d1b5c2e89 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/logging.h" #include "components/signin/public/identity_manager/access_token_info.h" +#include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/google_service_auth_error.h" namespace signin { @@ -16,15 +17,17 @@ namespace signin { PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher( const std::string& oauth_consumer_name, IdentityManager* identity_manager, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, - Mode mode) + Mode mode, + ConsentLevel consent) : oauth_consumer_name_(oauth_consumer_name), identity_manager_(identity_manager), scopes_(scopes), callback_(std::move(callback)), access_token_retried_(false), - mode_(mode) { + mode_(mode), + consent_(consent) { if (mode_ == Mode::kImmediate || AreCredentialsAvailable()) { StartAccessTokenRequest(); return; @@ -36,12 +39,16 @@ PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher( identity_manager_observer_.Add(identity_manager_); } -PrimaryAccountAccessTokenFetcher::~PrimaryAccountAccessTokenFetcher() {} +PrimaryAccountAccessTokenFetcher::~PrimaryAccountAccessTokenFetcher() = default; + +CoreAccountId PrimaryAccountAccessTokenFetcher::GetAccountId() const { + return identity_manager_->GetPrimaryAccountId(consent_); +} bool PrimaryAccountAccessTokenFetcher::AreCredentialsAvailable() const { DCHECK_EQ(Mode::kWaitUntilAvailable, mode_); - return identity_manager_->HasPrimaryAccountWithRefreshToken(); + return identity_manager_->HasAccountWithRefreshToken(GetAccountId()); } void PrimaryAccountAccessTokenFetcher::StartAccessTokenRequest() { @@ -63,7 +70,7 @@ void PrimaryAccountAccessTokenFetcher::StartAccessTokenRequest() { // token available. AccessTokenFetcher used in // |kWaitUntilRefreshTokenAvailable| mode would guarantee only the latter. access_token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForAccount( - identity_manager_->GetPrimaryAccountId(), oauth_consumer_name_, scopes_, + GetAccountId(), oauth_consumer_name_, scopes_, base::BindOnce( &PrimaryAccountAccessTokenFetcher::OnAccessTokenFetchComplete, base::Unretained(this)), @@ -72,6 +79,23 @@ void PrimaryAccountAccessTokenFetcher::StartAccessTokenRequest() { void PrimaryAccountAccessTokenFetcher::OnPrimaryAccountSet( const CoreAccountInfo& primary_account_info) { + // When sync consent is not required the signin is handled in + // OnUnconsentedPrimaryAccountChanged() below. + if (consent_ == ConsentLevel::kNotRequired) + return; + DCHECK(!primary_account_info.account_id.empty()); + ProcessSigninStateChange(); +} + +void PrimaryAccountAccessTokenFetcher::OnUnconsentedPrimaryAccountChanged( + const CoreAccountInfo& primary_account_info) { + // This method is called after both SetPrimaryAccount and + // SetUnconsentedPrimaryAccount. + if (consent_ == ConsentLevel::kSync) + return; + // We're only interested when the account is set. + if (primary_account_info.account_id.empty()) + return; ProcessSigninStateChange(); } diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h index 7f09bc179f7..233116ea169 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h @@ -11,8 +11,10 @@ #include "base/macros.h" #include "base/scoped_observer.h" #include "components/signin/public/identity_manager/access_token_fetcher.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" -#include "services/identity/public/cpp/scope_set.h" +#include "components/signin/public/identity_manager/scope_set.h" +#include "google_apis/gaia/core_account_id.h" class GoogleServiceAuthError; @@ -67,7 +69,7 @@ struct AccessTokenInfo; // // introducing wrapper API surfaces. // MyClass::StartAccessTokenRequestForPrimaryAccount() { // // Choose scopes to obtain for the access token. -// identity::ScopeSet scopes; +// ScopeSet scopes; // scopes.insert(GaiaConstants::kMyFirstScope); // scopes.insert(GaiaConstants::kMySecondScope); @@ -149,11 +151,14 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { // the request completes (successful or not). If the // PrimaryAccountAccessTokenFetcher is destroyed before the process completes, // the callback is not called. + // |consent| defaults to kSync because historically having an "authenticated" + // account was tied to browser sync. See ./README.md. PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name, IdentityManager* identity_manager, - const identity::ScopeSet& scopes, + const ScopeSet& scopes, AccessTokenFetcher::TokenCallback callback, - Mode mode); + Mode mode, + ConsentLevel consent = ConsentLevel::kSync); ~PrimaryAccountAccessTokenFetcher() override; @@ -161,6 +166,10 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { bool access_token_request_retried() { return access_token_retried_; } private: + // Returns the primary account ID. If consent is |kNotRequired| this may be + // the "unconsented" primary account ID. + CoreAccountId GetAccountId() const; + // Returns true iff there is a primary account with a refresh token. Should // only be called in mode |kWaitUntilAvailable|. bool AreCredentialsAvailable() const; @@ -170,6 +179,8 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { // IdentityManager::Observer implementation. void OnPrimaryAccountSet( const CoreAccountInfo& primary_account_info) override; + void OnUnconsentedPrimaryAccountChanged( + const CoreAccountInfo& primary_account_info) override; void OnRefreshTokenUpdatedForAccount( const CoreAccountInfo& account_info) override; @@ -183,7 +194,7 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { std::string oauth_consumer_name_; IdentityManager* identity_manager_; - identity::ScopeSet scopes_; + ScopeSet scopes_; // Per the contract of this class, it is allowed for clients to delete this // object as part of the invocation of |callback_|. Hence, this object must @@ -202,6 +213,8 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { Mode mode_; + const ConsentLevel consent_; + DISALLOW_COPY_AND_ASSIGN(PrimaryAccountAccessTokenFetcher); }; diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc index 2f9bfa34e85..dfa9378bf44 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc @@ -11,6 +11,7 @@ #include "base/test/mock_callback.h" #include "base/test/task_environment.h" #include "components/signin/public/identity_manager/access_token_info.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -37,8 +38,8 @@ void OnAccessTokenFetchComplete( } // namespace -class PrimaryAccountAccessTokenFetcherTest : public testing::Test, - public IdentityManager::Observer { +class PrimaryAccountAccessTokenFetcherTest + : public testing::TestWithParam<ConsentLevel> { public: using TestTokenCallback = StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>; @@ -48,15 +49,24 @@ class PrimaryAccountAccessTokenFetcherTest : public testing::Test, base::Time::Now() + base::TimeDelta::FromHours(1), "id_token") {} - ~PrimaryAccountAccessTokenFetcherTest() override {} + ~PrimaryAccountAccessTokenFetcherTest() override = default; std::unique_ptr<PrimaryAccountAccessTokenFetcher> CreateFetcher( AccessTokenFetcher::TokenCallback callback, - PrimaryAccountAccessTokenFetcher::Mode mode) { + PrimaryAccountAccessTokenFetcher::Mode mode, + ConsentLevel consent) { std::set<std::string> scopes{"scope"}; return std::make_unique<PrimaryAccountAccessTokenFetcher>( "test_consumer", identity_test_env_.identity_manager(), scopes, - std::move(callback), mode); + std::move(callback), mode, consent); + } + + // Creates a fetcher with sync consent based on the test param. + std::unique_ptr<PrimaryAccountAccessTokenFetcher> CreateFetcher( + AccessTokenFetcher::TokenCallback callback, + PrimaryAccountAccessTokenFetcher::Mode mode) { + ConsentLevel consent = GetParam(); + return CreateFetcher(std::move(callback), mode, consent); } IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; } @@ -79,7 +89,7 @@ class PrimaryAccountAccessTokenFetcherTest : public testing::Test, AccessTokenInfo access_token_info_; }; -TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotShouldReturnAccessToken) { +TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotShouldReturnAccessToken) { TestTokenCallback callback; CoreAccountId account_id = SignIn(); @@ -98,7 +108,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotShouldReturnAccessToken) { access_token_info().id_token); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, WaitAndRetryShouldReturnAccessToken) { TestTokenCallback callback; @@ -119,7 +129,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, access_token_info().id_token); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { TestTokenCallback callback; CoreAccountId account_id = SignIn(); @@ -138,7 +148,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { access_token_info().id_token); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenSignedOut) { +TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenSignedOut) { base::RunLoop run_loop; // Signed out -> we should get called back. @@ -152,7 +162,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenSignedOut) { run_loop.Run(); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenNoRefreshToken) { base::RunLoop run_loop; @@ -169,7 +179,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, run_loop.Run(); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, WaitAndRetryNoCallbackWhenSignedOut) { TestTokenCallback callback; @@ -180,11 +190,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); } -// Tests related to waiting for sign-in don't apply on ChromeOS (it doesn't have -// that concept). -#if !defined(OS_CHROMEOS) - -TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForSignIn) { +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForSignIn) { TestTokenCallback callback; // Not signed in, so this should wait for a sign-in to complete. @@ -206,9 +212,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForSignIn) { EXPECT_FALSE(fetcher->access_token_request_retried()); } -#endif // !OS_CHROMEOS - -TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForRefreshToken) { +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForRefreshToken) { TestTokenCallback callback; CoreAccountId account_id = @@ -235,7 +239,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForRefreshToken) { EXPECT_FALSE(fetcher->access_token_request_retried()); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldIgnoreRefreshTokensForOtherAccounts) { TestTokenCallback callback; @@ -254,7 +258,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, identity_test_env()->MakeAccountAvailable(account_id.ToString() + "3"); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, OneShotCanceledAccessTokenRequest) { CoreAccountId account_id = SignIn(); @@ -274,7 +278,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, WaitAndRetryCanceledAccessTokenRequest) { TestTokenCallback callback; @@ -299,7 +303,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, access_token_info().id_token); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequestOnlyOnce) { TestTokenCallback callback; @@ -327,7 +331,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, #if !defined(OS_CHROMEOS) -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotRetryCanceledAccessTokenRequestIfSignedOut) { TestTokenCallback callback; @@ -352,7 +356,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, #endif // !OS_CHROMEOS -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotRetryCanceledAccessTokenRequestIfRefreshTokenRevoked) { TestTokenCallback callback; @@ -373,7 +377,7 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, identity_test_env()->RemoveRefreshTokenForPrimaryAccount(); } -TEST_F(PrimaryAccountAccessTokenFetcherTest, +TEST_P(PrimaryAccountAccessTokenFetcherTest, ShouldNotRetryFailedAccessTokenRequest) { TestTokenCallback callback; @@ -395,4 +399,74 @@ TEST_F(PrimaryAccountAccessTokenFetcherTest, GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); } +// The above tests all use a consented primary account, so they should all work +// whether or not sync consent is required. +INSTANTIATE_TEST_SUITE_P(All, + PrimaryAccountAccessTokenFetcherTest, + testing::Values(ConsentLevel::kNotRequired, + ConsentLevel::kSync)); + +#if defined(OS_CHROMEOS) +// Chrome OS can directly set the unconsented primary account during login, +// so it has additional tests. +TEST_F(PrimaryAccountAccessTokenFetcherTest, + UnconsentedPrimaryAccountWithSyncConsentNotRequired) { + TestTokenCallback callback; + + // Simulate login. + identity_test_env()->MakeUnconsentedPrimaryAccountAvailable("me@gmail.com"); + + // Perform an immediate fetch with consent not required. + auto fetcher = CreateFetcher( + callback.Get(), PrimaryAccountAccessTokenFetcher::Mode::kImmediate, + ConsentLevel::kNotRequired); + + // We should get called back with the token. + EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + UnconsentedPrimaryAccountWithSyncConsentRequired) { + TestTokenCallback callback; + + // Simulate login. + identity_test_env()->MakeUnconsentedPrimaryAccountAvailable("me@gmail.com"); + + // Try an immediate fetch with consent required. + auto fetcher = CreateFetcher( + callback.Get(), PrimaryAccountAccessTokenFetcher::Mode::kImmediate, + ConsentLevel::kSync); + + // No token request generated because the account isn't consented. + EXPECT_FALSE(identity_test_env()->IsAccessTokenRequestPending()); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldWaitForUnconsentedAccountLogin) { + TestTokenCallback callback; + + // Not logged in, so the fetcher waits for an account to become available. + auto fetcher = + CreateFetcher(callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable, + ConsentLevel::kNotRequired); + EXPECT_FALSE(identity_test_env()->IsAccessTokenRequestPending()); + + // Simulate login. + identity_test_env()->MakeUnconsentedPrimaryAccountAvailable("me@gmail.com"); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token); +} +#endif // defined(OS_CHROMEOS) + } // namespace signin diff --git a/chromium/components/signin/public/identity_manager/primary_account_mutator.h b/chromium/components/signin/public/identity_manager/primary_account_mutator.h index 0b23bde235c..6dfab8d8e2b 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_mutator.h +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator.h @@ -60,6 +60,19 @@ class PrimaryAccountMutator { virtual bool SetPrimaryAccount(const CoreAccountId& account_id) = 0; #if defined(OS_CHROMEOS) + // Revokes sync consent from the primary account. The primary account must + // have sync consent. After the call a primary account will remain but it will + // not have sync consent. + // TODO(https://crbug.com/1046746): Support non-Chrome OS platforms. + virtual void RevokeSyncConsent() = 0; + + // Sets the account with |account_id| as the unconsented primary account + // (i.e. without implying browser sync consent). Requires that the account + // is known by the IdentityManager. See README.md for details on the meaning + // of "unconsented". + virtual void SetUnconsentedPrimaryAccount( + const CoreAccountId& account_id) = 0; + // Updates the info of the account corresponding to (|gaia_id|, |email|), // marks it as the primary account, and returns whether the operation // succeeded or not. Currently, this method is guaranteed to succeed. diff --git a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc index f56a6d81b7e..780d722b140 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc @@ -11,11 +11,14 @@ #include "base/test/task_environment.h" #include "components/signin/public/base/signin_metrics.h" #include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/platform_test.h" +using signin::ConsentLevel; + namespace { // Constants used by the different tests. @@ -244,7 +247,7 @@ TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount) { // enable those preconditions on that platform #if !defined(OS_CHROMEOS) // Checks that setting the primary account fails if the account is not known by -// the identity service. +// the identity system. TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_NoAccount) { base::test::TaskEnvironment task_environment; signin::IdentityTestEnvironment environment; @@ -498,3 +501,32 @@ TEST_F(PrimaryAccountMutatorTest, RemoveAccountExpectation::kRemovePrimary, AuthExpectation::kAuthError); } #endif // !defined(OS_CHROMEOS) + +#if defined(OS_CHROMEOS) +TEST_F(PrimaryAccountMutatorTest, RevokeSyncConsent) { + base::test::TaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + signin::IdentityManager* identity_manager = environment.identity_manager(); + + class Observer : public signin::IdentityManager::Observer { + public: + void OnPrimaryAccountCleared(const CoreAccountInfo& info) override { + ++primary_account_cleared_; + } + + int primary_account_cleared_ = 0; + } observer; + identity_manager->AddObserver(&observer); + + environment.MakePrimaryAccountAvailable(kPrimaryAccountEmail); + ASSERT_TRUE(identity_manager->HasPrimaryAccount(ConsentLevel::kSync)); + EXPECT_EQ(0, observer.primary_account_cleared_); + + identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent(); + EXPECT_FALSE(identity_manager->HasPrimaryAccount(ConsentLevel::kSync)); + EXPECT_TRUE(identity_manager->HasPrimaryAccount(ConsentLevel::kNotRequired)); + EXPECT_EQ(1, observer.primary_account_cleared_); + + identity_manager->RemoveObserver(&observer); +} +#endif // defined(OS_CHROMEOS) diff --git a/chromium/components/signin/public/identity_manager/scope_set.h b/chromium/components/signin/public/identity_manager/scope_set.h new file mode 100644 index 00000000000..4f01c5d42ff --- /dev/null +++ b/chromium/components/signin/public/identity_manager/scope_set.h @@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SCOPE_SET_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SCOPE_SET_H_ + +#include <set> +#include <string> + +namespace signin { + +using ScopeSet = std::set<std::string>; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SCOPE_SET_H_ diff --git a/chromium/components/signin/public/webdata/BUILD.gn b/chromium/components/signin/public/webdata/BUILD.gn index a301f77272d..b805aa0c636 100644 --- a/chromium/components/signin/public/webdata/BUILD.gn +++ b/chromium/components/signin/public/webdata/BUILD.gn @@ -24,9 +24,7 @@ static_library("webdata") { source_set("unit_tests") { testonly = true - sources = [ - "token_service_table_unittest.cc", - ] + sources = [ "token_service_table_unittest.cc" ] deps = [ ":webdata", diff --git a/chromium/components/signin/public/webdata/token_web_data.cc b/chromium/components/signin/public/webdata/token_web_data.cc index 9ecd6b68624..9d6077da0ab 100644 --- a/chromium/components/signin/public/webdata/token_web_data.cc +++ b/chromium/components/signin/public/webdata/token_web_data.cc @@ -72,10 +72,9 @@ TokenResult::~TokenResult() {} TokenWebData::TokenWebData( scoped_refptr<WebDatabaseService> wdbs, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> db_task_runner, - const ProfileErrorCallback& callback) - : WebDataServiceBase(wdbs, callback, ui_task_runner), - token_backend_(new TokenWebDataBackend(db_task_runner)) {} + scoped_refptr<base::SingleThreadTaskRunner> db_task_runner) + : WebDataServiceBase(wdbs, std::move(ui_task_runner)), + token_backend_(new TokenWebDataBackend(std::move(db_task_runner))) {} void TokenWebData::SetTokenForService(const std::string& service, const std::string& token) { @@ -106,7 +105,7 @@ WebDataServiceBase::Handle TokenWebData::GetAllTokens( TokenWebData::TokenWebData( scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, scoped_refptr<base::SingleThreadTaskRunner> db_task_runner) - : WebDataServiceBase(nullptr, ProfileErrorCallback(), ui_task_runner), - token_backend_(new TokenWebDataBackend(db_task_runner)) {} + : WebDataServiceBase(nullptr, std::move(ui_task_runner)), + token_backend_(new TokenWebDataBackend(std::move(db_task_runner))) {} TokenWebData::~TokenWebData() {} diff --git a/chromium/components/signin/public/webdata/token_web_data.h b/chromium/components/signin/public/webdata/token_web_data.h index 2992fe6caef..680b904ed7c 100644 --- a/chromium/components/signin/public/webdata/token_web_data.h +++ b/chromium/components/signin/public/webdata/token_web_data.h @@ -48,8 +48,7 @@ class TokenWebData : public WebDataServiceBase { public: TokenWebData(scoped_refptr<WebDatabaseService> wdbs, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> db_task_runner, - const ProfileErrorCallback& callback); + scoped_refptr<base::SingleThreadTaskRunner> db_task_runner); TokenWebData(scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, scoped_refptr<base::SingleThreadTaskRunner> db_task_runner); |