diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 10:22:43 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:36:28 +0000 |
commit | 271a6c3487a14599023a9106329505597638d793 (patch) | |
tree | e040d58ffc86c1480b79ca8528020ca9ec919bf8 /chromium/components/signin | |
parent | 7b2ffa587235a47d4094787d72f38102089f402a (diff) | |
download | qtwebengine-chromium-271a6c3487a14599023a9106329505597638d793.tar.gz |
BASELINE: Update Chromium to 77.0.3865.59
Change-Id: I1e89a5f3b009a9519a6705102ad65c92fe736f21
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/signin')
219 files changed, 17184 insertions, 4442 deletions
diff --git a/chromium/components/signin/DEPS b/chromium/components/signin/DEPS index 63cf12deb31..8001065552e 100644 --- a/chromium/components/signin/DEPS +++ b/chromium/components/signin/DEPS @@ -1,20 +1,25 @@ include_rules = [ + "+chromeos/components/account_manager", "+components/content_settings", "+components/google/core", + "+components/image_fetcher/core", "+components/keyed_service/core", "+components/os_crypt", "+components/prefs", "+components/sync_preferences", + # Subdirectories of //components/signin must explicitly allow deps on each + # other based on the conceptual deps structure. + "-components/signin", + "+components/signin/public", + "+components/user_manager", "+components/webdata/common", "+crypto", "+google_apis/gaia", "+grit", # For generated headers "+net", + "+services/identity/public", + "+services/network/public", + "+services/network/test", "+sql", + "+ui/gfx", ] - -specific_include_rules = { - "test_signin_client\.h": [ - "+services/network/test" - ] -} diff --git a/chromium/components/signin/OWNERS b/chromium/components/signin/OWNERS index 184a152c6fd..e4b8e297b87 100644 --- a/chromium/components/signin/OWNERS +++ b/chromium/components/signin/OWNERS @@ -1,6 +1,7 @@ bsazonov@chromium.org droger@chromium.org msarda@chromium.org +sdefresne@chromium.org # TEAM: chrome-signin@chromium.org # COMPONENT: Services>SignIn diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index d2700ec7b52..4221020aca2 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -2,136 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/buildflag_header.gni") import("//components/signin/features.gni") if (is_android) { import("//build/config/android/rules.gni") } -buildflag_header("signin_buildflags") { - header = "signin_buildflags.h" - flags = [ - "ENABLE_DICE_SUPPORT=$enable_dice_support", - "ENABLE_MIRROR=$enable_mirror", - ] -} - -# This target contains code that will be shared between the Identity Service -# implementation and its client library/clients. Currently, this code is used -# both by IdentityManager and by its clients. -static_library("shared") { - sources = [ - "account_consistency_method.cc", - "account_consistency_method.h", - "account_info.cc", - "account_info.h", - "account_info_util.cc", - "account_info_util.h", - "avatar_icon_util.cc", - "avatar_icon_util.h", - "identity_utils.cc", - "identity_utils.h", - "set_accounts_in_cookie_result.h", - "signin_metrics.cc", - "signin_metrics.h", - "signin_pref_names.cc", - "signin_pref_names.h", - "signin_switches.cc", - "signin_switches.h", - "ubertoken_fetcher.cc", - "ubertoken_fetcher.h", - ] - deps = [ - ":signin_buildflags", - "//components/account_id", - "//components/prefs:prefs", - "//third_party/icu:icui18n", - "//third_party/re2", - "//ui/gfx", - "//url", - ] - public_deps = [ - "//base", - - # TODO(blundell): Analyze this dependency. - "//google_apis", - ] -} - -# This target contains what will become the core of the Identity Service -# implementation. It currently forms the core of the IdentityManager -# implementation (//services/identity/public/cpp/identity_manager.*). We are -# actively converting the codebase away from using the code in this target -# directly to using IdentityManager (tracked in https://crbug.com/796544). Do -# not add code to this target without consulting with blundell@chromium.org. -static_library("internals") { - sources = [ - "account_fetcher_service.cc", - "account_fetcher_service.h", - "account_info_fetcher.cc", - "account_info_fetcher.h", - "account_tracker_service.cc", - "account_tracker_service.h", - "child_account_info_fetcher_android.cc", - "child_account_info_fetcher_android.h", - "device_id_helper.cc", - "device_id_helper.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", - "oauth_multilogin_token_fetcher.h", - "profile_oauth2_token_service.cc", - "profile_oauth2_token_service.h", - "profile_oauth2_token_service_delegate_chromeos.cc", - "profile_oauth2_token_service_delegate_chromeos.h", - "signin_client.cc", - "signin_client.h", - "signin_manager.cc", - "signin_manager.h", - "signin_manager_base.cc", - "signin_manager_base.h", - "ubertoken_fetcher_impl.cc", - "ubertoken_fetcher_impl.h", - ] - - if (is_chromeos) { - sources -= [ "signin_manager.cc" ] - } - - deps = [ - ":shared", - ":signin_buildflags", - "//base", - "//components/image_fetcher/core", - "//components/keyed_service/core", - "//components/prefs", - "//google_apis", - "//net", - "//services/network/public/cpp", - "//services/network/public/mojom", - "//ui/gfx", - ] - - if (is_android) { - deps += [ "android:jni_headers" ] - } - - if (is_chromeos) { - deps += [ - "//chromeos/components/account_manager", - - # TODO(crbug.com/816954): Remove this line when Account Manager is - # launched. - "//chromeos/constants", - ] - } -} - +# This target contains code that is used by clients of //components/signin. static_library("browser") { sources = [ "about_signin_internals.cc", @@ -152,14 +29,12 @@ static_library("browser") { "dice_account_reconcilor_delegate.h", "dice_header_helper.cc", "dice_header_helper.h", - "identity_manager_wrapper.cc", - "identity_manager_wrapper.h", "mice_account_reconcilor_delegate.cc", "mice_account_reconcilor_delegate.h", "mirror_account_reconcilor_delegate.cc", "mirror_account_reconcilor_delegate.h", - "mutable_profile_oauth2_token_service_delegate.cc", - "mutable_profile_oauth2_token_service_delegate.h", + "multilogin_parameters.cc", + "multilogin_parameters.h", "signin_error_controller.cc", "signin_error_controller.h", "signin_header_helper.cc", @@ -174,24 +49,18 @@ static_library("browser") { "signin_status_metrics_provider_base.h", "signin_status_metrics_provider_delegate.cc", "signin_status_metrics_provider_delegate.h", - "webdata/token_service_table.cc", - "webdata/token_service_table.h", - "webdata/token_web_data.cc", - "webdata/token_web_data.h", ] configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] public_deps = [ - ":internals", - ":shared", - ":signin_buildflags", "//base", - "//components/account_id", "//components/content_settings/core/browser", "//components/content_settings/core/common", "//components/keyed_service/core", "//components/prefs", + "//components/signin/public/base", + "//components/signin/public/base:signin_buildflags", "//google_apis", "//net", "//services/network/public/cpp", @@ -203,9 +72,9 @@ static_library("browser") { "//components/google/core/browser", "//components/metrics", "//components/os_crypt", + "//components/signin/public/identity_manager", "//components/webdata/common", "//crypto", - "//services/identity/public/cpp", "//services/network/public/cpp", "//skia", "//sql", @@ -237,104 +106,51 @@ static_library("browser") { } } -# This target contains test support that backs the test support for -# IdentityManager (and eventually likely for the Identity Service). We are -# actively converting the codebase away from using the code in this target -# directly to using IdentityManager (tracked in https://crbug.com/796544). Do -# not add code to this target without consulting with blundell@chromium.org. -static_library("internals_test_support") { - testonly = true - sources = [ - "fake_profile_oauth2_token_service.cc", - "fake_profile_oauth2_token_service.h", - - # TODO(https://crbug.com/907782): Move list_accounts_test_utils to - # //services/identity/public/cpp once FakeGCMS no longer depends on it. - "list_accounts_test_utils.cc", - "list_accounts_test_utils.h", - "test_signin_client.cc", - "test_signin_client.h", - ] - - deps = [ - "//base/test:test_support", - "//components/prefs", - "//google_apis:test_support", - ] - - public_deps = [ - ":internals", - ":shared", - "//base", - "//services/network:test_support", - ] -} - source_set("unit_tests") { testonly = true sources = [ - "account_info_unittest.cc", - "account_info_util_unittest.cc", "account_investigator_unittest.cc", "account_reconcilor_delegate_unittest.cc", "account_reconcilor_unittest.cc", - "account_tracker_service_unittest.cc", - "avatar_icon_util_unittest.cc", - "device_id_helper_unittest.cc", "dice_account_reconcilor_delegate_unittest.cc", - "gaia_cookie_manager_service_unittest.cc", "mice_account_reconcilor_delegate_unittest.cc", - "mutable_profile_oauth2_token_service_delegate_unittest.cc", - "oauth_multilogin_helper_unittest.cc", - "oauth_multilogin_token_fetcher_unittest.cc", - "profile_oauth2_token_service_delegate_chromeos_unittest.cc", "signin_error_controller_unittest.cc", "signin_header_helper_unittest.cc", "signin_investigator_unittest.cc", - "signin_manager_unittest.cc", - "signin_metrics_unittest.cc", "signin_status_metrics_provider_unittest.cc", - "ubertoken_fetcher_impl_unittest.cc", - "webdata/token_service_table_unittest.cc", ] deps = [ ":browser", - ":internals_test_support", - ":signin_buildflags", + "//base", "//base/test:test_support", "//components/content_settings/core/browser", - "//components/image_fetcher/core", - "//components/image_fetcher/core:test_support", - "//components/os_crypt:test_support", "//components/prefs", "//components/prefs:test_support", - "//components/sync_preferences", + "//components/signin/public/base", + "//components/signin/public/base:signin_buildflags", + "//components/signin/public/base:test_support", + "//components/signin/public/identity_manager", + "//components/signin/public/identity_manager:test_support", "//components/sync_preferences:test_support", - "//components/webdata/common", - "//google_apis:test_support", + "//google_apis", "//net:test_support", - "//services/identity/public/cpp:test_support", + "//net/traffic_annotation:test_support", + "//services/network:test_support", "//testing/gmock", "//testing/gtest", - "//ui/gfx:test_support", + "//url", ] if (is_chromeos) { sources -= [ "account_investigator_unittest.cc", - "signin_manager_unittest.cc", "signin_status_metrics_provider_unittest.cc", ] - - deps += [ "//chromeos/components/account_manager" ] } if (is_android) { - sources += [ - "consistency_cookie_manager_unittest.cc", - "oauth2_token_service_delegate_android_unittest.cc", - ] + sources += [ "consistency_cookie_manager_unittest.cc" ] } if (!enable_dice_support) { diff --git a/chromium/components/signin/core/browser/DEPS b/chromium/components/signin/core/browser/DEPS index 6aa48343a03..d9ef433896d 100644 --- a/chromium/components/signin/core/browser/DEPS +++ b/chromium/components/signin/core/browser/DEPS @@ -1,16 +1,3 @@ include_rules = [ - "+chromeos/components/account_manager", - # TODO(crbug.com/816954): Remove this line when Account Manager is - # launched. - "+chromeos/constants", - "+components/account_id", - "+components/image_fetcher/core", "+components/metrics", - "+jni", - "+mojo/public", - "+services/identity/public", - "+services/network/public", - "+services/network/test", - "+third_party/re2", - "+ui/gfx", ] diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc index b3e4d988d4c..3032fad8753 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.cc +++ b/chromium/components/signin/core/browser/about_signin_internals.cc @@ -11,21 +11,20 @@ #include "base/command_line.h" #include "base/hash/hash.h" -#include "base/i18n/time_formatting.h" #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" +#include "base/time/time_to_iso8601.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_switches.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_switches.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/diagnostics_provider.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/load_credentials_state.h" #include "net/base/backoff_entry.h" -#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h" -#include "services/identity/public/cpp/diagnostics_provider.h" -#include "services/identity/public/cpp/identity_manager.h" namespace { @@ -62,10 +61,6 @@ std::string GetGaiaCookiesStateAsString(const GaiaCookiesState state) { } } -std::string GetTimeStr(base::Time time) { - return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)); -} - base::ListValue* AddSection(base::ListValue* parent_list, const std::string& title) { auto section = std::make_unique<base::DictionaryValue>(); @@ -117,24 +112,27 @@ std::string SigninStatusFieldToLabel( } std::string TokenServiceLoadCredentialsStateToLabel( - OAuth2TokenServiceDelegate::LoadCredentialsState state) { + signin::LoadCredentialsState state) { switch (state) { - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED: + case signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED: return "Load credentials not started"; - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS: + case signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS: return "Load credentials in progress"; - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: + case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: return "Load credentials finished with success"; - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: + case signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED: + return "Load credentials failed with datase cannot be opened error"; + case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: return "Load credentials failed with database errors"; - case OAuth2TokenServiceDelegate:: + case signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS: return "Load credentials failed with decrypt errors"; - case OAuth2TokenServiceDelegate:: + case signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT: return "Load credentials failed with no refresh token for signed in " "account"; - case OAuth2TokenServiceDelegate:: + case signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS: return "Load credentials failed with unknown errors"; } @@ -159,10 +157,12 @@ std::string SigninStatusFieldToLabel( } #endif // !defined (OS_CHROMEOS) +// It's quite unfortunate that |time| is saved in prefs as a string instead of +// base::Time because any change of the format would create inconsistency. void SetPref(PrefService* prefs, signin_internals_util::TimedSigninStatusField field, - const std::string& time, - const std::string& value) { + const std::string& value, + const std::string& time) { std::string value_pref = SigninStatusFieldToString(field) + ".value"; std::string time_pref = SigninStatusFieldToString(field) + ".time"; prefs->SetString(value_pref, value); @@ -171,8 +171,8 @@ void SetPref(PrefService* prefs, void GetPref(PrefService* prefs, signin_internals_util::TimedSigninStatusField field, - std::string* time, - std::string* value) { + std::string* value, + std::string* time) { std::string value_pref = SigninStatusFieldToString(field) + ".value"; std::string time_pref = SigninStatusFieldToString(field) + ".time"; *value = prefs->GetString(value_pref); @@ -206,7 +206,7 @@ std::string GetAccountConsistencyDescription( } // anonymous namespace AboutSigninInternals::AboutSigninInternals( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninErrorController* signin_error_controller, signin::AccountConsistencyMethod account_consistency) : identity_manager_(identity_manager), @@ -231,8 +231,6 @@ signin_internals_util::TimedSigninStatusField& operator++( // static void AboutSigninInternals::RegisterPrefs(PrefRegistrySimple* user_prefs) { - // SigninManager information for about:signin-internals. - // TODO(rogerta): leaving untimed fields here for now because legacy // profiles still have these prefs. In three or four version from M43 // we can probably remove them. @@ -271,8 +269,7 @@ void AboutSigninInternals::NotifyTimedSigninFieldValueChanged( field_index < signin_status_.timed_signin_fields.size()); base::Time now = base::Time::NowFromSystemTime(); - std::string time_as_str = - base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now)); + std::string time_as_str = base::TimeToISO8601(now); TimedSigninStatusValue timed_value(value, time_as_str); signin_status_.timed_signin_fields[field_index] = timed_value; @@ -301,7 +298,7 @@ void AboutSigninInternals::RefreshSigninPrefs() { i < signin_internals_util::TIMED_FIELDS_END; ++i) { std::string time_str; std::string value_str; - GetPref(pref_service, i, &time_str, &value_str); + GetPref(pref_service, i, &value_str, &time_str); TimedSigninStatusValue value(value_str, time_str); signin_status_ .timed_signin_fields[i - signin_internals_util::TIMED_FIELDS_BEGIN] = @@ -362,7 +359,7 @@ std::unique_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() { } void AboutSigninInternals::OnAccessTokenRequested( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes) { TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes); @@ -377,7 +374,7 @@ void AboutSigninInternals::OnAccessTokenRequested( } void AboutSigninInternals::OnAccessTokenRequestCompleted( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes, GoogleServiceAuthError error, @@ -396,7 +393,7 @@ void AboutSigninInternals::OnAccessTokenRequestCompleted( } void AboutSigninInternals::OnRefreshTokenUpdatedForAccountFromSource( - const std::string& account_id, + const CoreAccountId& account_id, bool is_refresh_token_valid, const std::string& source) { RefreshTokenEvent event; @@ -410,7 +407,7 @@ void AboutSigninInternals::OnRefreshTokenUpdatedForAccountFromSource( } void AboutSigninInternals::OnRefreshTokenRemovedForAccountFromSource( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& source) { RefreshTokenEvent event; event.account_id = account_id; @@ -421,7 +418,7 @@ void AboutSigninInternals::OnRefreshTokenRemovedForAccountFromSource( void AboutSigninInternals::OnRefreshTokensLoaded() { RefreshTokenEvent event; - event.account_id = "All accounts"; + // event.account_id = CoreAccountId("All accounts"); event.type = AboutSigninInternals::RefreshTokenEventType::kAllTokensLoaded; signin_status_.AddRefreshTokenEvent(event); NotifyObservers(); @@ -432,7 +429,7 @@ void AboutSigninInternals::OnEndBatchOfRefreshTokenStateChanges() { } void AboutSigninInternals::OnAccessTokenRemovedFromCache( - const std::string& account_id, + const CoreAccountId& account_id, const identity::ScopeSet& scopes) { for (const std::unique_ptr<TokenInfo>& token : signin_status_.token_info_map[account_id]) { @@ -468,7 +465,7 @@ void AboutSigninInternals::OnPrimaryAccountCleared( } void AboutSigninInternals::OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) { if (error.state() != GoogleServiceAuthError::NONE) return; @@ -524,14 +521,14 @@ AboutSigninInternals::TokenInfo::ToValue() const { scopes_str += *it + "<br/>"; } token_info->SetString("scopes", scopes_str); - token_info->SetString("request_time", GetTimeStr(request_time)); + token_info->SetString("request_time", base::TimeToISO8601(request_time)); if (removed_) { token_info->SetString("status", "Token was revoked."); } else if (!receive_time.is_null()) { if (error == GoogleServiceAuthError::AuthErrorNone()) { bool token_expired = expiration_time < base::Time::Now(); - std::string expiration_time_string = GetTimeStr(expiration_time); + std::string expiration_time_string = base::TimeToISO8601(expiration_time); if (expiration_time.is_null()) { token_expired = false; expiration_time_string = "Expiration time not available"; @@ -540,7 +537,7 @@ AboutSigninInternals::TokenInfo::ToValue() const { if (token_expired) status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">"; base::StringAppendF(&status_str, "Received token at %s. Expire at %s", - GetTimeStr(receive_time).c_str(), + base::TimeToISO8601(receive_time).c_str(), expiration_time_string.c_str()); if (token_expired) base::StringAppendF(&status_str, "</p>"); @@ -579,7 +576,7 @@ AboutSigninInternals::SigninStatus::SigninStatus() AboutSigninInternals::SigninStatus::~SigninStatus() {} AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes) { for (const std::unique_ptr<TokenInfo>& token : token_info_map[account_id]) { @@ -599,7 +596,7 @@ void AboutSigninInternals::SigninStatus::AddRefreshTokenEvent( std::unique_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninErrorController* signin_error_controller, SigninClient* signin_client, signin::AccountConsistencyMethod account_consistency) { @@ -616,7 +613,7 @@ AboutSigninInternals::SigninStatus::ToValue( AddSectionEntry( basic_info, "Signin Status", identity_manager->HasPrimaryAccount() ? "Signed In" : "Not Signed In"); - OAuth2TokenServiceDelegate::LoadCredentialsState load_tokens_state = + signin::LoadCredentialsState load_tokens_state = identity_manager->GetDiagnosticsProvider() ->GetDetailedStateOfLoadingOfRefreshTokens(); AddSectionEntry(basic_info, "TokenService Load Status", @@ -629,7 +626,7 @@ AboutSigninInternals::SigninStatus::ToValue( CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo(); AddSectionEntry(basic_info, SigninStatusFieldToLabel(signin_internals_util::ACCOUNT_ID), - account_info.account_id); + account_info.account_id.id); AddSectionEntry(basic_info, SigninStatusFieldToLabel(signin_internals_util::GAIA_ID), account_info.gaia); @@ -637,7 +634,7 @@ AboutSigninInternals::SigninStatus::ToValue( SigninStatusFieldToLabel(signin_internals_util::USERNAME), account_info.email); if (signin_error_controller->HasError()) { - const std::string error_account_id = + const CoreAccountId error_account_id = signin_error_controller->error_account_id(); const base::Optional<AccountInfo> error_account_info = identity_manager @@ -645,7 +642,7 @@ AboutSigninInternals::SigninStatus::ToValue( error_account_id); AddSectionEntry(basic_info, "Auth Error", signin_error_controller->auth_error().ToString()); - AddSectionEntry(basic_info, "Auth Error Account Id", error_account_id); + AddSectionEntry(basic_info, "Auth Error Account Id", error_account_id.id); // The error_account_info optional should never be unset when we reach // this line (as we should have a refresh token, even if in an error @@ -684,15 +681,8 @@ AboutSigninInternals::SigninStatus::ToValue( if (cookie_requests_delay > base::TimeDelta()) { base::Time next_retry_time = base::Time::NowFromSystemTime() + cookie_requests_delay; - - std::string next_retry_time_as_str = - base::UTF16ToUTF8( - base::TimeFormatShortDateAndTime(next_retry_time)); - - AddSectionEntry(detailed_info, - "Cookie Manager Next Retry", - next_retry_time_as_str, - ""); + AddSectionEntry(detailed_info, "Cookie Manager Next Retry", + base::TimeToISO8601(next_retry_time), ""); } base::TimeDelta token_requests_delay = @@ -702,15 +692,8 @@ AboutSigninInternals::SigninStatus::ToValue( if (token_requests_delay > base::TimeDelta()) { base::Time next_retry_time = base::Time::NowFromSystemTime() + token_requests_delay; - - std::string next_retry_time_as_str = - base::UTF16ToUTF8( - base::TimeFormatShortDateAndTime(next_retry_time)); - - AddSectionEntry(detailed_info, - "Token Service Next Retry", - next_retry_time_as_str, - ""); + AddSectionEntry(detailed_info, "Token Service Next Retry", + base::TimeToISO8601(next_retry_time), ""); } #endif // !defined(OS_CHROMEOS) @@ -718,7 +701,7 @@ AboutSigninInternals::SigninStatus::ToValue( // Token information for all services. auto token_info = std::make_unique<base::ListValue>(); for (auto it = token_info_map.begin(); it != token_info_map.end(); ++it) { - base::ListValue* token_details = AddSection(token_info.get(), it->first); + base::ListValue* token_details = AddSection(token_info.get(), it->first.id); std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan); for (const std::unique_ptr<TokenInfo>& token : it->second) token_details->Append(token->ToValue()); @@ -755,8 +738,8 @@ AboutSigninInternals::SigninStatus::ToValue( auto refresh_token_events_value = std::make_unique<base::ListValue>(); for (const auto& event : refresh_token_events) { auto entry = std::make_unique<base::DictionaryValue>(); - entry->SetString("accountId", event.account_id); - entry->SetString("timestamp", GetTimeStr(event.timestamp)); + entry->SetString("accountId", event.account_id.id); + entry->SetString("timestamp", base::TimeToISO8601(event.timestamp)); entry->SetString("type", event.GetTypeAsString()); entry->SetString("source", event.source); refresh_token_events_value->Append(std::move(entry)); diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h index ccfd208c6f8..9b34c7d9895 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.h +++ b/chromium/components/signin/core/browser/about_signin_internals.h @@ -17,13 +17,13 @@ #include "base/values.h" #include "components/content_settings/core/browser/content_settings_observer.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_error_controller.h" #include "components/signin/core/browser/signin_internals_util.h" -#include "services/identity/public/cpp/identity_manager.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" -namespace identity { +namespace signin { struct AccountsInCookieJarInfo; } @@ -39,8 +39,8 @@ using TimedSigninStatusValue = std::pair<std::string, std::string>; class AboutSigninInternals : public KeyedService, public content_settings::Observer, SigninErrorController::Observer, - identity::IdentityManager::Observer, - identity::IdentityManager::DiagnosticsObserver { + signin::IdentityManager::Observer, + signin::IdentityManager::DiagnosticsObserver { public: class Observer { public: @@ -52,7 +52,7 @@ class AboutSigninInternals : public KeyedService, virtual void OnCookieAccountsFetched(const base::DictionaryValue* info) = 0; }; - AboutSigninInternals(identity::IdentityManager* identity_manager, + AboutSigninInternals(signin::IdentityManager* identity_manager, SigninErrorController* signin_error_controller, signin::AccountConsistencyMethod account_consistency); ~AboutSigninInternals() override; @@ -92,9 +92,9 @@ class AboutSigninInternals : public KeyedService, // } std::unique_ptr<base::DictionaryValue> GetSigninStatus(); - // identity::IdentityManager::Observer implementations. + // signin::IdentityManager::Observer implementations. void OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) override; private: @@ -131,7 +131,7 @@ class AboutSigninInternals : public KeyedService, std::string GetTypeAsString() const; const base::Time timestamp; - std::string account_id; + CoreAccountId account_id; RefreshTokenEventType type; std::string source; }; @@ -143,7 +143,7 @@ class AboutSigninInternals : public KeyedService, std::vector<TimedSigninStatusValue> timed_signin_fields; // Map account id to tokens associated to the account. - std::map<std::string, std::vector<std::unique_ptr<TokenInfo>>> + std::map<CoreAccountId, std::vector<std::unique_ptr<TokenInfo>>> token_info_map; // All the events that affected the refresh tokens. @@ -152,7 +152,7 @@ class AboutSigninInternals : public KeyedService, SigninStatus(); ~SigninStatus(); - TokenInfo* FindToken(const std::string& account_id, + TokenInfo* FindToken(const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes); @@ -176,29 +176,29 @@ class AboutSigninInternals : public KeyedService, // }], // } std::unique_ptr<base::DictionaryValue> ToValue( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninErrorController* signin_error_controller, SigninClient* signin_client, signin::AccountConsistencyMethod account_consistency); }; // IdentityManager::DiagnosticsObserver implementations. - void OnAccessTokenRequested(const std::string& account_id, + void OnAccessTokenRequested(const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes) override; - void OnAccessTokenRequestCompleted(const std::string& account_id, + void OnAccessTokenRequestCompleted(const CoreAccountId& account_id, const std::string& consumer_id, const identity::ScopeSet& scopes, GoogleServiceAuthError error, base::Time expiration_time) override; - void OnAccessTokenRemovedFromCache(const std::string& account_id, + void OnAccessTokenRemovedFromCache(const CoreAccountId& account_id, const identity::ScopeSet& scopes) override; void OnRefreshTokenUpdatedForAccountFromSource( - const std::string& account_id, + const CoreAccountId& account_id, bool is_refresh_token_valid, const std::string& source) override; void OnRefreshTokenRemovedForAccountFromSource( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& source) override; // IdentityManager::Observer implementations. @@ -225,7 +225,7 @@ class AboutSigninInternals : public KeyedService, const std::string& resource_identifier) override; // Weak pointer to the identity manager. - identity::IdentityManager* identity_manager_; + signin::IdentityManager* identity_manager_; // Weak pointer to the client. SigninClient* client_; diff --git a/chromium/components/signin/core/browser/account_investigator.cc b/chromium/components/signin/core/browser/account_investigator.cc index eaa7f06d29a..ba15883762a 100644 --- a/chromium/components/signin/core/browser/account_investigator.cc +++ b/chromium/components/signin/core/browser/account_investigator.cc @@ -13,11 +13,11 @@ #include "base/time/time.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h" using base::Time; using base::TimeDelta; @@ -43,7 +43,7 @@ const TimeDelta AccountInvestigator::kPeriodicReportingInterval = AccountInvestigator::AccountInvestigator( PrefService* pref_service, - identity::IdentityManager* identity_manager) + signin::IdentityManager* identity_manager) : pref_service_(pref_service), identity_manager_(identity_manager) {} AccountInvestigator::~AccountInvestigator() {} @@ -74,7 +74,7 @@ void AccountInvestigator::Shutdown() { } void AccountInvestigator::OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) { if (error != GoogleServiceAuthError::AuthErrorNone()) { // If we are pending periodic reporting, leave the flag set, and we will @@ -135,12 +135,12 @@ std::string AccountInvestigator::HashAccounts( std::transform(std::begin(signed_in_accounts), std::end(signed_in_accounts), std::back_inserter(sorted_ids), [](const ListedAccount& account) { - return std::string(kSignedInHashPrefix) + account.id; + return std::string(kSignedInHashPrefix) + account.id.id; }); std::transform(std::begin(signed_out_accounts), std::end(signed_out_accounts), std::back_inserter(sorted_ids), [](const ListedAccount& account) { - return std::string(kSignedOutHashPrefix) + account.id; + return std::string(kSignedOutHashPrefix) + account.id.id; }); std::sort(sorted_ids.begin(), sorted_ids.end()); std::ostringstream stream; diff --git a/chromium/components/signin/core/browser/account_investigator.h b/chromium/components/signin/core/browser/account_investigator.h index 0d8acc738fa..0a7b9538bab 100644 --- a/chromium/components/signin/core/browser/account_investigator.h +++ b/chromium/components/signin/core/browser/account_investigator.h @@ -11,7 +11,7 @@ #include "base/macros.h" #include "base/timer/timer.h" #include "components/keyed_service/core/keyed_service.h" -#include "services/identity/public/cpp/identity_manager.h" +#include "components/signin/public/identity_manager/identity_manager.h" struct CoreAccountInfo; class PrefRegistrySimple; @@ -21,9 +21,9 @@ namespace base { class Time; } // namespace base -namespace identity { +namespace signin { struct AccountsInCookieJarInfo; -} // namespace identity +} // namespace signin namespace signin_metrics { enum class AccountRelation; @@ -35,7 +35,7 @@ enum class ReportingType; // is to watch for changes in relation between Chrome and content area accounts // and emit metrics about their relation. class AccountInvestigator : public KeyedService, - public identity::IdentityManager::Observer { + public signin::IdentityManager::Observer { public: // The targeted interval to perform periodic reporting. If chrome is not // active at the end of an interval, reporting will be done as soon as @@ -43,7 +43,7 @@ class AccountInvestigator : public KeyedService, static const base::TimeDelta kPeriodicReportingInterval; AccountInvestigator(PrefService* pref_service, - identity::IdentityManager* identity_manager); + signin::IdentityManager* identity_manager); ~AccountInvestigator() override; static void RegisterPrefs(PrefRegistrySimple* registry); @@ -54,9 +54,9 @@ class AccountInvestigator : public KeyedService, // KeyedService: void Shutdown() override; - // identity::IdentityManager::Observer: + // signin::IdentityManager::Observer: void OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) override; private: @@ -109,7 +109,7 @@ class AccountInvestigator : public KeyedService, signin_metrics::ReportingType type); PrefService* pref_service_; - identity::IdentityManager* identity_manager_; + signin::IdentityManager* identity_manager_; // Handles invoking our periodic logic at the right time. As part of our // handling of this call we reset the timer for the next loop. diff --git a/chromium/components/signin/core/browser/account_investigator_unittest.cc b/chromium/components/signin/core/browser/account_investigator_unittest.cc index e6f5b19c144..c752ee46940 100644 --- a/chromium/components/signin/core/browser/account_investigator_unittest.cc +++ b/chromium/components/signin/core/browser/account_investigator_unittest.cc @@ -5,20 +5,18 @@ #include "components/signin/core/browser/account_investigator.h" #include <map> -#include <string> -#include <vector> #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_task_environment.h" -#include "base/timer/timer.h" #include "components/prefs/pref_registry_simple.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.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_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/cpp/identity_test_environment.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -39,7 +37,7 @@ class AccountInvestigatorTest : public testing::Test { ~AccountInvestigatorTest() override { investigator_.Shutdown(); } - identity::IdentityTestEnvironment* identity_test_env() { + signin::IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; } PrefService* pref_service() { return &prefs_; } @@ -138,7 +136,7 @@ class AccountInvestigatorTest : public testing::Test { base::test::ScopedTaskEnvironment task_environment_; sync_preferences::TestingPrefServiceSyncable prefs_; network::TestURLLoaderFactory test_url_loader_factory_; - identity::IdentityTestEnvironment identity_test_env_; + signin::IdentityTestEnvironment identity_test_env_; AccountInvestigator investigator_; std::map<ReportingType, std::string> suffix_ = { {ReportingType::PERIODIC, "_Periodic"}, @@ -164,9 +162,9 @@ AccountInfo ToAccountInfo(ListedAccount account) { // NOTE: IdentityTestEnvironment uses a prefix for generating gaia IDs: // "gaia_id_for_". For this reason, the tests prefix expected account IDs // used so that there is a match. -const std::string kGaiaId1 = identity::GetTestGaiaIdForEmail("1@mail.com"); -const std::string kGaiaId2 = identity::GetTestGaiaIdForEmail("2@mail.com"); -const std::string kGaiaId3 = identity::GetTestGaiaIdForEmail("3@mail.com"); +const std::string kGaiaId1 = signin::GetTestGaiaIdForEmail("1@mail.com"); +const std::string kGaiaId2 = signin::GetTestGaiaIdForEmail("2@mail.com"); +const std::string kGaiaId3 = signin::GetTestGaiaIdForEmail("3@mail.com"); const ListedAccount one(Account(kGaiaId1)); const ListedAccount two(Account(kGaiaId2)); @@ -263,7 +261,7 @@ TEST_F(AccountInvestigatorTest, SharedCookieJarReportWithAccount) { TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedError) { const HistogramTester histogram_tester; - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { /*accounts_are_fresh=*/true, just_one, no_accounts}; GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); investigator()->OnAccountsInCookieUpdated(accounts_in_cookie_jar_info, error); @@ -273,7 +271,7 @@ TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedError) { TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedOnChange) { const HistogramTester histogram_tester; - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { /*accounts_are_fresh=*/true, just_one, no_accounts}; investigator()->OnAccountsInCookieUpdated( accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone()); @@ -284,14 +282,14 @@ TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedOnChange) { TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedSigninOnly) { // Initial update to simulate the update on first-time-run. investigator()->OnAccountsInCookieUpdated( - identity::AccountsInCookieJarInfo(), + signin::AccountsInCookieJarInfo(), GoogleServiceAuthError::AuthErrorNone()); const HistogramTester histogram_tester; identity_test_env()->SetPrimaryAccount("1@mail.com"); pref_service()->SetString(prefs::kGaiaCookieHash, Hash(just_one, no_accounts)); - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { /*accounts_are_fresh=*/true, just_one, no_accounts}; investigator()->OnAccountsInCookieUpdated( accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone()); @@ -305,7 +303,7 @@ TEST_F(AccountInvestigatorTest, OnGaiaAccountsInCookieUpdatedSigninSignOutOfContent) { const HistogramTester histogram_tester; identity_test_env()->SetPrimaryAccount("1@mail.com"); - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { /*accounts_are_fresh=*/true, just_one, no_accounts}; investigator()->OnAccountsInCookieUpdated( accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone()); @@ -355,7 +353,7 @@ TEST_F(AccountInvestigatorTest, TryPeriodicReportStale) { std::string email("f@bar.com"); identity_test_env()->SetCookieAccounts( - {{email, identity::GetTestGaiaIdForEmail(email)}}); + {{email, signin::GetTestGaiaIdForEmail(email)}}); EXPECT_FALSE(*periodic_pending()); ExpectSharedReportHistograms(ReportingType::PERIODIC, histogram_tester, diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc index a2f98a11ed4..f609c05f688 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.cc +++ b/chromium/components/signin/core/browser/account_reconcilor.cc @@ -20,19 +20,18 @@ #include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" #include "components/signin/core/browser/consistency_cookie_manager_base.h" -#include "components/signin/core/browser/set_accounts_in_cookie_result.h" -#include "components/signin/core/browser/signin_buildflags.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_metrics.h" +#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" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/accounts_mutator.h" +#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/cpp/accounts_cookie_mutator.h" -#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h" -#include "services/identity/public/cpp/accounts_mutator.h" #if defined(OS_ANDROID) #include "components/signin/core/browser/consistency_cookie_manager_android.h" @@ -60,7 +59,7 @@ bool AccountEqualToFunc::operator()(const gaia::ListedAccount& other) const { return account_.valid == other.valid && account_.id == other.id; } -gaia::ListedAccount AccountForId(const std::string& account_id) { +gaia::ListedAccount AccountForId(const CoreAccountId& account_id) { gaia::ListedAccount account; account.id = account_id; return account; @@ -81,9 +80,9 @@ std::vector<gaia::ListedAccount> FilterUnverifiedAccounts( // Revokes tokens for all accounts in chrome_accounts but the primary account. // Returns true if tokens were revoked, and false if the function did nothing. bool RevokeAllSecondaryTokens( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, signin::AccountReconcilorDelegate::RevokeTokenOption revoke_option, - const std::string& primary_account, + const CoreAccountId& primary_account, bool is_account_consistency_enforced, signin_metrics::SourceForRefreshTokenOperation source) { bool token_revoked = false; @@ -92,7 +91,7 @@ bool RevokeAllSecondaryTokens( return false; for (const CoreAccountInfo& account_info : identity_manager->GetAccountsWithRefreshTokens()) { - std::string account(account_info.account_id); + CoreAccountId account = account_info.account_id; if (account == primary_account) continue; bool should_revoke = false; @@ -127,7 +126,7 @@ bool RevokeAllSecondaryTokens( } // Pick the account will become first after this reconcile is finished. -std::string PickFirstGaiaAccount( +CoreAccountId PickFirstGaiaAccount( const signin::MultiloginParameters& parameters, const std::vector<gaia::ListedAccount>& gaia_accounts) { if (parameters.mode == @@ -135,18 +134,18 @@ std::string PickFirstGaiaAccount( !gaia_accounts.empty()) { return gaia_accounts[0].id; } - return parameters.accounts_to_send.empty() ? "" + return parameters.accounts_to_send.empty() ? CoreAccountId() : parameters.accounts_to_send[0]; } // Returns true if gaia_accounts contains an invalid account that is unknown to // the identity manager. bool HasUnknownInvalidAccountInCookie( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, const std::vector<gaia::ListedAccount>& gaia_accounts) { for (const gaia::ListedAccount& account : gaia_accounts) { if (!account.valid && - !identity_manager->HasAccountWithRefreshToken(account.gaia_id)) { + !identity_manager->HasAccountWithRefreshToken(account.id)) { return true; } } @@ -183,7 +182,7 @@ AccountReconcilor::ScopedSyncedDataDeletion::~ScopedSyncedDataDeletion() { } AccountReconcilor::AccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, std::unique_ptr<signin::AccountReconcilorDelegate> delegate) : delegate_(std::move(delegate)), @@ -200,8 +199,7 @@ AccountReconcilor::AccountReconcilor( account_reconcilor_lock_count_(0), reconcile_on_unblock_(false), timer_(new base::OneShotTimer), - state_(signin_metrics::ACCOUNT_RECONCILOR_OK), - weak_factory_(this) { + state_(signin_metrics::ACCOUNT_RECONCILOR_OK) { VLOG(1) << "AccountReconcilor::AccountReconcilor"; DCHECK(delegate_); delegate_->set_reconcilor(this); @@ -251,11 +249,8 @@ void AccountReconcilor::SetIsWKHTTPSystemCookieStoreEnabled(bool is_enabled) { void AccountReconcilor::EnableReconcile() { SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED); RegisterWithAllDependencies(); -#if !defined(OS_IOS) - // TODO(droger): Investigate why this breaks tests on iOS. if (IsIdentityManagerReady()) StartReconcile(); -#endif // !defined(OS_IOS) } void AccountReconcilor::DisableReconcile(bool logout_all_accounts) { @@ -379,7 +374,7 @@ void AccountReconcilor::OnErrorStateOfRefreshTokenUpdatedForAccount( identity_manager_->GetAccountsCookieMutator()->TriggerCookieJarUpdate(); } -void AccountReconcilor::PerformMergeAction(const std::string& account_id) { +void AccountReconcilor::PerformMergeAction(const CoreAccountId& account_id) { reconcile_is_noop_ = false; if (!delegate_->IsAccountConsistencyEnforced()) { MarkAccountAsAddedToCookie(account_id); @@ -399,14 +394,20 @@ void AccountReconcilor::PerformSetCookiesAction( OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult::kSuccess); return; } + VLOG(1) << "AccountReconcilor::PerformSetCookiesAction: " - << base::JoinString(parameters.accounts_to_send, " "); + << base::JoinString(ToStringList(parameters.accounts_to_send), " "); // TODO (https://crbug.com/890321): pass mode to GaiaCookieManagerService. // // Using Unretained is safe here because the CookieManagerService outlives // the AccountReconcilor. + // TODO(triploblastic): Remove this vector once account_reconcilor and + // related classes has been refactored to use CoreAccountId. + std::vector<CoreAccountId> accounts_to_send; + for (const auto& account : parameters.accounts_to_send) + accounts_to_send.push_back(CoreAccountId(account)); identity_manager_->GetAccountsCookieMutator()->SetAccountsInCookie( - parameters.accounts_to_send, delegate_->GetGaiaApiSource(), + accounts_to_send, delegate_->GetGaiaApiSource(), base::BindOnce(&AccountReconcilor::OnSetAccountsInCookieCompleted, base::Unretained(this))); } @@ -461,7 +462,7 @@ void AccountReconcilor::StartReconcile() { base::Unretained(this))); } - const std::string& account_id = identity_manager_->GetPrimaryAccountId(); + const CoreAccountId& account_id = identity_manager_->GetPrimaryAccountId(); if (identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState( account_id) && delegate_->ShouldAbortReconcileIfPrimaryHasError()) { @@ -475,7 +476,7 @@ void AccountReconcilor::StartReconcile() { // Rely on the IdentityManager to manage calls to and responses from // ListAccounts. - identity::AccountsInCookieJarInfo accounts_in_cookie_jar = + signin::AccountsInCookieJarInfo accounts_in_cookie_jar = identity_manager_->GetAccountsInCookieJar(); if (accounts_in_cookie_jar.accounts_are_fresh) { OnAccountsInCookieUpdated( @@ -485,8 +486,8 @@ void AccountReconcilor::StartReconcile() { } void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( - const std::string& primary_account, - const std::vector<std::string>& chrome_accounts, + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts) { DCHECK(IsMultiloginEndpointEnabled()); DCHECK(!set_accounts_in_progress_); @@ -538,7 +539,7 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( // be already consistent. DCHECK(!CookieNeedsUpdate(parameters_for_multilogin, gaia_accounts)); DCHECK_NE(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING, state_); - std::string first_gaia_account_after_reconcile = + CoreAccountId first_gaia_account_after_reconcile = PickFirstGaiaAccount(parameters_for_multilogin, gaia_accounts); delegate_->OnReconcileFinished(first_gaia_account_after_reconcile, reconcile_is_noop_); @@ -547,7 +548,7 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( } void AccountReconcilor::OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) { const std::vector<gaia::ListedAccount>& accounts( accounts_in_cookie_jar_info.signed_in_accounts); @@ -590,7 +591,7 @@ void AccountReconcilor::OnAccountsInCookieUpdated( << "Ignore " << accounts.size() - verified_gaia_accounts.size() << " unverified account(s)."; - std::string primary_account = identity_manager_->GetPrimaryAccountId(); + CoreAccountId primary_account = identity_manager_->GetPrimaryAccountId(); // Revoking tokens for secondary accounts causes the AccountTracker to // completely remove them from Chrome. // Revoking the token for the primary account is not supported (it should be @@ -603,11 +604,11 @@ void AccountReconcilor::OnAccountsInCookieUpdated( signin_metrics::SourceForRefreshTokenOperation:: kAccountReconcilor_GaiaCookiesUpdated); - std::vector<std::string> chrome_accounts = + std::vector<CoreAccountId> chrome_accounts = LoadValidAccountsFromTokenService(); if (delegate_->ShouldAbortReconcileIfPrimaryHasError() && - !base::ContainsValue(chrome_accounts, primary_account)) { + !base::Contains(chrome_accounts, primary_account)) { VLOG(1) << "Primary account has error, abort."; DCHECK(is_reconcile_started_); AbortReconcile(); @@ -628,7 +629,8 @@ void AccountReconcilor::OnAccountsCookieDeletedByUserAction() { if (!delegate_->ShouldRevokeTokensOnCookieDeleted()) return; - const std::string& primary_account = identity_manager_->GetPrimaryAccountId(); + const CoreAccountId& primary_account = + identity_manager_->GetPrimaryAccountId(); // Revoke secondary tokens. RevokeAllSecondaryTokens( identity_manager_, AccountReconcilorDelegate::RevokeTokenOption::kRevoke, @@ -648,12 +650,12 @@ void AccountReconcilor::OnAccountsCookieDeletedByUserAction() { } } -std::vector<std::string> AccountReconcilor::LoadValidAccountsFromTokenService() - const { +std::vector<CoreAccountId> +AccountReconcilor::LoadValidAccountsFromTokenService() const { auto chrome_accounts_with_refresh_tokens = identity_manager_->GetAccountsWithRefreshTokens(); - std::vector<std::string> chrome_account_ids; + std::vector<CoreAccountId> chrome_account_ids; // Remove any accounts that have an error. There is no point in trying to // reconcile them, since it won't work anyway. If the list ends up being @@ -684,14 +686,17 @@ void AccountReconcilor::OnReceivedManageAccountsResponse( } void AccountReconcilor::FinishReconcile( - const std::string& primary_account, - const std::vector<std::string>& chrome_accounts, + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts) { VLOG(1) << "AccountReconcilor::FinishReconcile"; DCHECK(add_to_cookie_.empty()); DCHECK(delegate_->IsUnknownInvalidAccountInCookieAllowed()) << "Only supported in UPDATE mode"; + delegate_->MaybeLogInconsistencyReason(primary_account, chrome_accounts, + gaia_accounts, first_execution_); + size_t number_gaia_accounts = gaia_accounts.size(); // If there are any accounts in the gaia cookie but not in chrome, then // those accounts need to be removed from the cookie. This means we need @@ -699,12 +704,12 @@ void AccountReconcilor::FinishReconcile( int removed_from_cookie = 0; for (size_t i = 0; i < number_gaia_accounts; ++i) { if (gaia_accounts[i].valid && - !base::ContainsValue(chrome_accounts, gaia_accounts[i].id)) { + !base::Contains(chrome_accounts, gaia_accounts[i].id)) { ++removed_from_cookie; } } - std::string first_account = delegate_->GetFirstGaiaAccountForReconcile( + CoreAccountId first_account = delegate_->GetFirstGaiaAccountForReconcile( chrome_accounts, gaia_accounts, primary_account, first_execution_, removed_from_cookie > 0); bool first_account_mismatch = @@ -731,13 +736,13 @@ void AccountReconcilor::FinishReconcile( kAccountReconcilor_Reconcile); } else { // Create a list of accounts that need to be added to the Gaia cookie. - if (base::ContainsValue(chrome_accounts, first_account)) { + if (base::Contains(chrome_accounts, first_account)) { add_to_cookie_.push_back(first_account); } else { // If the first account is not empty and not in chrome_accounts, it is // impossible to rebuild it. It must be already the current default // account, and no logout can happen. - DCHECK_EQ(gaia_accounts[0].gaia_id, first_account); + DCHECK_EQ(gaia_accounts[0].id, first_account); DCHECK(!rebuild_cookie); } for (size_t i = 0; i < chrome_accounts.size(); ++i) { @@ -748,9 +753,9 @@ void AccountReconcilor::FinishReconcile( // For each account known to chrome, PerformMergeAction() if the account is // not already in the cookie jar or its state is invalid, or signal merge - // completed otherwise. Make a copy of |add_to_cookie_| since calls to - // OnAddAccountToCookieCompleted() will change the array. - std::vector<std::string> add_to_cookie_copy = add_to_cookie_; + // completed otherwise. Make a copy of |add_to_cookie_| since calls + // to OnAddAccountToCookieCompleted() will change the array. + std::vector<CoreAccountId> add_to_cookie_copy = add_to_cookie_; int added_to_cookie = 0; for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) { if (gaia_accounts.end() != @@ -843,7 +848,7 @@ void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { // Remove the account from the list that is being merged. bool AccountReconcilor::MarkAccountAsAddedToCookie( - const std::string& account_id) { + const CoreAccountId& account_id) { for (auto i = add_to_cookie_.begin(); i != add_to_cookie_.end(); ++i) { if (account_id == *i) { add_to_cookie_.erase(i); @@ -854,16 +859,7 @@ bool AccountReconcilor::MarkAccountAsAddedToCookie( } bool AccountReconcilor::IsIdentityManagerReady() { -#if defined(OS_CHROMEOS) - // TODO(droger): ChromeOS should use the same logic as other platforms. See - // https://crbug.com/749535 - // On ChromeOS, there are cases where the IdentityManager is never fully - // initialized and AreAllCredentialsLoaded() always return false. - return identity_manager_->AreRefreshTokensLoaded() || - (identity_manager_->GetAccountsWithRefreshTokens().size() > 0); -#else return identity_manager_->AreRefreshTokensLoaded(); -#endif } void AccountReconcilor::OnSetAccountsInCookieCompleted( @@ -894,7 +890,7 @@ void AccountReconcilor::OnSetAccountsInCookieCompleted( } void AccountReconcilor::OnAddAccountToCookieCompleted( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { VLOG(1) << "AccountReconcilor::OnAddAccountToCookieCompleted: " << "Account added: " << account_id << ", " @@ -1015,9 +1011,9 @@ bool AccountReconcilor::CookieNeedsUpdate( } // Maybe some accounts in cookies are not valid and need refreshing. - std::set<std::string> accounts_to_send_set( + std::set<CoreAccountId> accounts_to_send_set( parameters.accounts_to_send.begin(), parameters.accounts_to_send.end()); - std::set<std::string> existing_accounts_set; + std::set<CoreAccountId> existing_accounts_set; for (const gaia::ListedAccount& account : existing_accounts) { if (account.valid) existing_accounts_set.insert(account.id); diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h index 9801137824e..387cd6f6ed6 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.h +++ b/chromium/components/signin/core/browser/account_reconcilor.h @@ -23,11 +23,11 @@ #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_header_helper.h" -#include "components/signin/core/browser/signin_metrics.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/identity_manager/identity_manager.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/cpp/identity_manager.h" // Enables usage of Gaia Auth Multilogin endpoint for identity consistency. extern const base::Feature kUseMultiloginEndpoint; @@ -42,7 +42,7 @@ class SigninClient; class AccountReconcilor : public KeyedService, public content_settings::Observer, - public identity::IdentityManager::Observer { + public signin::IdentityManager::Observer { public: // When an instance of this class exists, the account reconcilor is suspended. // It will automatically restart when all instances of Lock have been @@ -94,7 +94,7 @@ class AccountReconcilor : public KeyedService, }; AccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, std::unique_ptr<signin::AccountReconcilorDelegate> delegate); ~AccountReconcilor() override; @@ -146,6 +146,8 @@ class AccountReconcilor : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorEndpointParamTest, ProfileAlreadyConnected); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestTable, TableRowTest); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestTable, + InconsistencyReasonLogging); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestDiceMultilogin, TableRowTest); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestMirrorMultilogin, TableRowTest); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestMiceMultilogin, TableRowTest); @@ -254,7 +256,7 @@ class AccountReconcilor : public KeyedService, // All actions with side effects, only doing meaningful work if account // consistency is enabled. Virtual so that they can be overridden in tests. - virtual void PerformMergeAction(const std::string& account_id); + virtual void PerformMergeAction(const CoreAccountId& account_id); virtual void PerformLogoutAllAccountsAction(); virtual void PerformSetCookiesAction( const signin::MultiloginParameters& parameters); @@ -262,18 +264,18 @@ class AccountReconcilor : public KeyedService, // Used during periodic reconciliation. void StartReconcile(); // |gaia_accounts| are the accounts in the Gaia cookie. - void FinishReconcile(const std::string& primary_account, - const std::vector<std::string>& chrome_accounts, + void FinishReconcile(const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts); void AbortReconcile(); void CalculateIfReconcileIsDone(); void ScheduleStartReconcileIfChromeAccountsChanged(); // Returns the list of valid accounts from the TokenService. - std::vector<std::string> LoadValidAccountsFromTokenService() const; + std::vector<CoreAccountId> LoadValidAccountsFromTokenService() const; // Note internally that this |account_id| is added to the cookie jar. - bool MarkAccountAsAddedToCookie(const std::string& account_id); + bool MarkAccountAsAddedToCookie(const CoreAccountId& account_id); // The reconcilor only starts when the token service is ready. bool IsIdentityManagerReady(); @@ -284,24 +286,23 @@ class AccountReconcilor : public KeyedService, ContentSettingsType content_type, const std::string& resource_identifier) override; - - // Overridden from identity::IdentityManager::Observer. + // Overridden from signin::IdentityManager::Observer. void OnEndBatchOfRefreshTokenStateChanges() override; void OnRefreshTokensLoaded() override; void OnErrorStateOfRefreshTokenUpdatedForAccount( const CoreAccountInfo& account_info, const GoogleServiceAuthError& error) override; void OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) override; void OnAccountsCookieDeletedByUserAction() override; void FinishReconcileWithMultiloginEndpoint( - const std::string& primary_account, - const std::vector<std::string>& chrome_accounts, + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts); - void OnAddAccountToCookieCompleted(const std::string& account_id, + void OnAddAccountToCookieCompleted(const CoreAccountId& account_id, const GoogleServiceAuthError& error); void OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult result); @@ -330,7 +331,7 @@ class AccountReconcilor : public KeyedService, std::unique_ptr<signin::AccountReconcilorDelegate> delegate_; // The IdentityManager associated with this reconcilor. - identity::IdentityManager* identity_manager_; + signin::IdentityManager* identity_manager_; // The SigninClient associated with this reconcilor. SigninClient* client_; @@ -361,8 +362,8 @@ class AccountReconcilor : public KeyedService, bool reconcile_is_noop_; // Used during reconcile action. - std::vector<std::string> add_to_cookie_; // Progress of AddAccount calls. - bool set_accounts_in_progress_; // Progress of SetAccounts calls. + std::vector<CoreAccountId> add_to_cookie_; // Progress of AddAccount calls. + bool set_accounts_in_progress_; // Progress of SetAccounts calls. bool chrome_accounts_changed_; // Used for the Lock. @@ -398,7 +399,7 @@ class AccountReconcilor : public KeyedService, std::unique_ptr<signin::ConsistencyCookieManagerBase> consistency_cookie_manager_; - base::WeakPtrFactory<AccountReconcilor> weak_factory_; + base::WeakPtrFactory<AccountReconcilor> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(AccountReconcilor); }; diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc index 1b5d13c290d..3cf1a3bbb7a 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc @@ -18,6 +18,12 @@ bool AccountReconcilorDelegate::IsAccountConsistencyEnforced() const { return false; } +void AccountReconcilorDelegate::MaybeLogInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const {} + gaia::GaiaSource AccountReconcilorDelegate::GetGaiaApiSource() const { NOTREACHED() << "Reconcile is not enabled, no Gaia API calls should be made."; return gaia::GaiaSource::kChrome; @@ -27,25 +33,25 @@ bool AccountReconcilorDelegate::ShouldAbortReconcileIfPrimaryHasError() const { return false; } -std::string AccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, +CoreAccountId AccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const { - return std::string(); + return CoreAccountId(); } MultiloginParameters AccountReconcilorDelegate::CalculateParametersForMultilogin( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& primary_account, const std::vector<gaia::ListedAccount>& gaia_accounts, bool first_execution, bool primary_has_error) const { const gaia::MultiloginMode mode = CalculateModeForReconcile( gaia_accounts, primary_account, first_execution, primary_has_error); - const std::vector<std::string> accounts_to_send = + const std::vector<CoreAccountId> accounts_to_send = GetChromeAccountsForReconcile(chrome_accounts, primary_account, gaia_accounts, mode); return {mode, accounts_to_send}; @@ -53,38 +59,38 @@ AccountReconcilorDelegate::CalculateParametersForMultilogin( gaia::MultiloginMode AccountReconcilorDelegate::CalculateModeForReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const { return gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER; } -std::vector<std::string> +std::vector<CoreAccountId> AccountReconcilorDelegate::ReorderChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& first_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& first_account, const std::vector<gaia::ListedAccount>& gaia_accounts) const { // Gaia only supports kMaxGaiaAccounts. Multilogin and MergeSession calls // which go above this count will fail. const int kMaxGaiaAccounts = 10; DCHECK(!first_account.empty()); - DCHECK(base::ContainsValue(chrome_accounts, first_account)); + DCHECK(base::Contains(chrome_accounts, first_account)); // Ordered list of accounts, this is the result of this function. - std::vector<std::string> ordered_accounts; + std::vector<CoreAccountId> ordered_accounts; ordered_accounts.reserve(gaia_accounts.size()); // Set of accounts that must be added to ordered_accounts. - std::set<std::string> chrome_accounts_set(chrome_accounts.begin(), - chrome_accounts.end()); + std::set<CoreAccountId> chrome_accounts_set(chrome_accounts.begin(), + chrome_accounts.end()); // Start from the gaia accounts. for (const gaia::ListedAccount& account : gaia_accounts) ordered_accounts.push_back(account.id); // Keep only accounts that are in chrome_accounts_set. - for (std::string& account : ordered_accounts) { + for (CoreAccountId& account : ordered_accounts) { if (chrome_accounts_set.find(account) == chrome_accounts_set.end()) - account = std::string(); + account = CoreAccountId(); else chrome_accounts_set.erase(account); } @@ -101,7 +107,7 @@ AccountReconcilorDelegate::ReorderChromeAccountsForReconcile( // The first account was not already in the cookies, add it in the first // empty spot, or at the end if there is no available spot. first_account_it = std::find(ordered_accounts.begin(), - ordered_accounts.end(), std::string()); + ordered_accounts.end(), CoreAccountId()); if (first_account_it == ordered_accounts.end()) { first_account_it = ordered_accounts.insert(first_account_it, first_account); @@ -115,7 +121,7 @@ AccountReconcilorDelegate::ReorderChromeAccountsForReconcile( // Add the remaining chrome accounts. // First in empty spots. auto remaining_accounts_it = chrome_accounts_set.begin(); - for (std::string& account : ordered_accounts) { + for (CoreAccountId& account : ordered_accounts) { if (remaining_accounts_it == chrome_accounts_set.end()) break; if (account.empty()) { @@ -138,7 +144,7 @@ AccountReconcilorDelegate::ReorderChromeAccountsForReconcile( ordered_accounts.pop_back(); // Find next empty slot. compacting_it = - std::find(compacting_it, ordered_accounts.end(), std::string()); + std::find(compacting_it, ordered_accounts.end(), CoreAccountId()); // Swap it with the last element. if (compacting_it != ordered_accounts.end()) std::swap(*compacting_it, ordered_accounts.back()); @@ -155,13 +161,13 @@ AccountReconcilorDelegate::ReorderChromeAccountsForReconcile( return ordered_accounts; } -std::vector<std::string> +std::vector<CoreAccountId> AccountReconcilorDelegate::GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& primary_account, const std::vector<gaia::ListedAccount>& gaia_accounts, const gaia::MultiloginMode mode) const { - return std::vector<std::string>(); + return std::vector<CoreAccountId>(); } AccountReconcilorDelegate::RevokeTokenOption diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.h b/chromium/components/signin/core/browser/account_reconcilor_delegate.h index e75cfe0cda1..ab7cf8a88e4 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.h @@ -9,7 +9,7 @@ #include <vector> #include "base/time/time.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" +#include "components/signin/core/browser/multilogin_parameters.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -43,6 +43,13 @@ class AccountReconcilorDelegate { // changes to the accounts are made. Defaults to false. virtual bool IsAccountConsistencyEnforced() const; + // Computes inconsistency reason and uploads it to UMA. + virtual void MaybeLogInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const; + // Returns the value to set in the "source" parameter for Gaia API calls. virtual gaia::GaiaSource GetGaiaApiSource() const; @@ -57,17 +64,17 @@ class AccountReconcilorDelegate { // |will_logout| is true if the reconcilor will perform a logout no matter // what is returned by this function. // Only used with MergeSession. - virtual std::string GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, + virtual CoreAccountId GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const; // Returns a pair of mode and accounts to send to Mutilogin endpoint. MultiloginParameters CalculateParametersForMultilogin( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& primary_account, const std::vector<gaia::ListedAccount>& gaia_accounts, bool first_execution, bool primary_has_error) const; @@ -88,7 +95,7 @@ class AccountReconcilorDelegate { // |OnReconcileFinished| is always called at the end of reconciliation, even // when there is an error (except in cases where reconciliation times out // before finishing, see |GetReconcileTimeout|). - virtual void OnReconcileFinished(const std::string& first_account, + virtual void OnReconcileFinished(const CoreAccountId& first_account, bool reconcile_is_noop) {} // Returns the desired timeout for account reconciliation. If reconciliation @@ -125,17 +132,17 @@ class AccountReconcilorDelegate { // Aplhabetical order is used to break ties. // Note: the input order of the accounts in |chrome_accounts| does not matter // (different orders yield to the same result). - std::vector<std::string> ReorderChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& first_account, + std::vector<CoreAccountId> ReorderChromeAccountsForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& first_account, const std::vector<gaia::ListedAccount>& gaia_accounts) const; private: // Reorders chrome accounts in the order they should appear in cookies with // respect to existing cookies. - virtual std::vector<std::string> GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + virtual 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; @@ -143,7 +150,7 @@ class AccountReconcilorDelegate { // accounts (e.g. on mobile or on stratup). Default is UPDATE. virtual gaia::MultiloginMode CalculateModeForReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const; diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_delegate_unittest.cc index c8bdedf51de..218386d9725 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate_unittest.cc @@ -107,7 +107,7 @@ class AccountReconcilorDelegateTest std::vector<gaia::ListedAccount> gaia_accounts; for (const char& c : account_string) { gaia::ListedAccount account; - account.id = std::string(1, c); + account.id = CoreAccountId(std::string(1, c)); gaia_accounts.push_back(account); } return gaia_accounts; @@ -116,24 +116,27 @@ class AccountReconcilorDelegateTest TEST_P(AccountReconcilorDelegateTest, ReorderChromeAccountsForReconcile) { // Decode test parameters. - std::string first_account = std::string(1, GetParam().first_account); - std::vector<std::string> chrome_accounts; - for (int i = 0; GetParam().chrome_accounts[i] != '\0'; ++i) - chrome_accounts.push_back(std::string(1, GetParam().chrome_accounts[i])); - ASSERT_TRUE(base::ContainsValue(chrome_accounts, first_account)) + CoreAccountId first_account = + CoreAccountId(std::string(1, GetParam().first_account)); + std::vector<CoreAccountId> chrome_accounts; + for (int i = 0; GetParam().chrome_accounts[i] != '\0'; ++i) { + chrome_accounts.push_back( + CoreAccountId(std::string(1, GetParam().chrome_accounts[i]))); + } + ASSERT_TRUE(base::Contains(chrome_accounts, first_account)) << "Invalid test parameter."; std::vector<gaia::ListedAccount> gaia_accounts = GaiaAccountsFromString(GetParam().gaia_accounts); // Reorder the accounts. - std::vector<std::string> order = ReorderChromeAccountsForReconcile( + std::vector<CoreAccountId> order = ReorderChromeAccountsForReconcile( chrome_accounts, first_account, gaia_accounts); // Check results. std::string order_as_string; - for (const std::string& account : order) { - ASSERT_EQ(1u, account.size()); - order_as_string += account; + for (const CoreAccountId& account : order) { + ASSERT_EQ(1u, account.id.size()); + order_as_string += account.id; } EXPECT_EQ(GetParam().expected_order, order_as_string); diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc index a1d22a5f74e..2269dd882ff 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc @@ -19,27 +19,26 @@ #include "base/time/time.h" #include "base/timer/mock_timer.h" #include "build/build_config.h" -#include "components/image_fetcher/core/fake_image_decoder.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/signin/core/browser/account_consistency_method.h" +#include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/core/browser/list_accounts_test_utils.h" #include "components/signin/core/browser/mice_account_reconcilor_delegate.h" #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h" -#include "components/signin/core/browser/set_accounts_in_cookie_result.h" -#include "components/signin/core/browser/signin_buildflags.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "components/sync_preferences/pref_service_syncable.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/list_accounts_test_utils.h" +#include "components/signin/public/base/signin_buildflags.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/identity_test_environment.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/set_accounts_in_cookie_result.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" #include "net/url_request/test_url_fetcher_factory.h" -#include "services/identity/public/cpp/identity_test_environment.h" -#include "services/identity/public/cpp/identity_test_utils.h" -#include "services/identity/public/cpp/primary_account_mutator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -67,16 +66,16 @@ class SpyReconcilorDelegate : public signin::AccountReconcilorDelegate { bool ShouldAbortReconcileIfPrimaryHasError() const override { return true; } - std::string GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, + CoreAccountId GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const override { return primary_account; } - void OnReconcileFinished(const std::string& first_account, + void OnReconcileFinished(const CoreAccountId& first_account, bool reconcile_is_noop) override { ++num_reconcile_finished_calls_; } @@ -98,7 +97,7 @@ class SpyReconcilorDelegate : public signin::AccountReconcilorDelegate { class DummyAccountReconcilorWithDelegate : public AccountReconcilor { public: DummyAccountReconcilorWithDelegate( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, signin::AccountConsistencyMethod account_consistency) : AccountReconcilor( @@ -116,7 +115,7 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor { // Takes ownership of |delegate|. // gmock can't work with move only parameters. DummyAccountReconcilorWithDelegate( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, signin::AccountReconcilorDelegate* delegate) : AccountReconcilor( @@ -132,7 +131,7 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor { static std::unique_ptr<signin::AccountReconcilorDelegate> CreateAccountReconcilorDelegate( SigninClient* signin_client, - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, signin::AccountConsistencyMethod account_consistency) { switch (account_consistency) { case signin::AccountConsistencyMethod::kMirror: @@ -163,23 +162,23 @@ class MockAccountReconcilor : public testing::StrictMock<DummyAccountReconcilorWithDelegate> { public: explicit MockAccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, signin::AccountConsistencyMethod account_consistency); explicit MockAccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, std::unique_ptr<signin::AccountReconcilorDelegate> delegate); - MOCK_METHOD1(PerformMergeAction, void(const std::string& account_id)); + MOCK_METHOD1(PerformMergeAction, void(const CoreAccountId& account_id)); MOCK_METHOD0(PerformLogoutAllAccountsAction, void()); MOCK_METHOD1(PerformSetCookiesAction, void(const signin::MultiloginParameters& parameters)); }; MockAccountReconcilor::MockAccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, signin::AccountConsistencyMethod account_consistency) : testing::StrictMock<DummyAccountReconcilorWithDelegate>( @@ -188,7 +187,7 @@ MockAccountReconcilor::MockAccountReconcilor( account_consistency) {} MockAccountReconcilor::MockAccountReconcilor( - identity::IdentityManager* identity_manager, + signin::IdentityManager* identity_manager, SigninClient* client, std::unique_ptr<signin::AccountReconcilorDelegate> delegate) : testing::StrictMock<DummyAccountReconcilorWithDelegate>( @@ -208,7 +207,7 @@ struct Cookie { // Converts CookieParams to ListedAccounts. gaia::ListedAccount ListedAccountFromCookieParams( const signin::CookieParams& params, - const std::string& account_id) { + const CoreAccountId& account_id) { gaia::ListedAccount listed_account; listed_account.id = account_id; listed_account.email = params.email; @@ -227,9 +226,14 @@ class AccountReconcilorTest : public ::testing::Test { AccountReconcilorTest(); ~AccountReconcilorTest() override; - identity::IdentityTestEnvironment* identity_test_env() { + signin::IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; } + + base::test::ScopedTaskEnvironment* task_environment() { + return &task_environment_; + } + TestSigninClient* test_signin_client() { return &test_signin_client_; } base::HistogramTester* histogram_tester() { return &histogram_tester_; } @@ -239,11 +243,11 @@ class AccountReconcilorTest : public ::testing::Test { AccountInfo ConnectProfileToAccount(const std::string& email); - std::string PickAccountIdForAccount(const std::string& gaia_id, - const std::string& username); + CoreAccountId PickAccountIdForAccount(const std::string& gaia_id, + const std::string& username); void SimulateAddAccountToCookieCompleted(AccountReconcilor* reconcilor, - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error); void SimulateCookieContentSettingsChanged( @@ -267,7 +271,7 @@ class AccountReconcilorTest : public ::testing::Test { signin::AccountConsistencyMethod account_consistency_; sync_preferences::TestingPrefServiceSyncable pref_service_; TestSigninClient test_signin_client_; - identity::IdentityTestEnvironment identity_test_env_; + signin::IdentityTestEnvironment identity_test_env_; std::unique_ptr<MockAccountReconcilor> mock_reconcilor_; base::HistogramTester histogram_tester_; @@ -327,15 +331,14 @@ INSTANTIATE_TEST_SUITE_P(Dice_Mirror, signin::AccountConsistencyMethod::kMirror)); AccountReconcilorTest::AccountReconcilorTest() - : account_consistency_(signin::AccountConsistencyMethod::kDisabled), + : task_environment_( + base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME), + account_consistency_(signin::AccountConsistencyMethod::kDisabled), test_signin_client_(&pref_service_, &test_url_loader_factory_), identity_test_env_(/*test_url_loader_factory=*/nullptr, &pref_service_, account_consistency_, &test_signin_client_) { - pref_service_.registry()->RegisterBooleanPref( - prefs::kTokenServiceDiceCompatible, false); - signin::SetListAccountsResponseHttpNotFound(&test_url_loader_factory_); // The reconcilor should not be built before the test can set the account @@ -375,7 +378,7 @@ AccountInfo AccountReconcilorTest::ConnectProfileToAccount( return account_info; } -std::string AccountReconcilorTest::PickAccountIdForAccount( +CoreAccountId AccountReconcilorTest::PickAccountIdForAccount( const std::string& gaia_id, const std::string& username) { return identity_test_env()->identity_manager()->PickAccountIdForAccount( @@ -384,7 +387,7 @@ std::string AccountReconcilorTest::PickAccountIdForAccount( void AccountReconcilorTest::SimulateAddAccountToCookieCompleted( AccountReconcilor* reconcilor, - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { reconcilor->OnAddAccountToCookieCompleted(account_id, error); } @@ -429,6 +432,8 @@ struct AccountReconcilorTestTableParam { const char* gaia_api_calls_multilogin; const char* tokens_after_reconcile_multilogin; const char* cookies_after_reconcile_multilogin; + // Int represents AccountReconcilorDelegate::InconsistencyReason. + const int inconsistency_reason; }; std::vector<AccountReconcilorTestTableParam> GenerateTestCasesFromParams( @@ -476,11 +481,11 @@ class AccountReconcilorTestTable AccountReconcilorTestTable() { accounts_['A'] = {"a@gmail.com", - identity::GetTestGaiaIdForEmail("a@gmail.com")}; + signin::GetTestGaiaIdForEmail("a@gmail.com")}; accounts_['B'] = {"b@gmail.com", - identity::GetTestGaiaIdForEmail("b@gmail.com")}; + signin::GetTestGaiaIdForEmail("b@gmail.com")}; accounts_['C'] = {"c@gmail.com", - identity::GetTestGaiaIdForEmail("c@gmail.com")}; + signin::GetTestGaiaIdForEmail("c@gmail.com")}; } // Build Tokens from string. @@ -531,7 +536,7 @@ class AccountReconcilorTestTable bool authenticated_account_found = false; for (const Token& token : tokens) { - std::string account_id = + CoreAccountId account_id = PickAccountIdForAccount(token.gaia_id, token.email); EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id)); EXPECT_EQ( @@ -552,7 +557,7 @@ class AccountReconcilorTestTable ParseTokenString(GetParam().tokens); Token primary_account; for (const Token& token : tokens_before_reconcile) { - std::string account_id; + CoreAccountId account_id; if (token.is_authenticated) { account_id = ConnectProfileToAccount(token.email).account_id; } else { @@ -560,7 +565,7 @@ class AccountReconcilorTestTable identity_test_env()->MakeAccountAvailable(token.email).account_id; } if (token.has_error) { - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), account_id, GoogleServiceAuthError( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); @@ -686,12 +691,12 @@ std::vector<Cookie> FakeSetAccountsInCookie( std::vector<Cookie> cookies_after_reconcile; if (parameters.mode == gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER) { - for (const std::string& account : parameters.accounts_to_send) { + for (const CoreAccountId& account : parameters.accounts_to_send) { cookies_after_reconcile.push_back({account, true}); } } else { - std::set<std::string> accounts_set; - for (const std::string& account : parameters.accounts_to_send) { + std::set<CoreAccountId> accounts_set; + for (const CoreAccountId& account : parameters.accounts_to_send) { accounts_set.insert(account); } cookies_after_reconcile = cookies_before_reconcile; @@ -703,7 +708,7 @@ std::vector<Cookie> FakeSetAccountsInCookie( param.is_valid = false; } } - for (const std::string& account : accounts_set) { + for (const CoreAccountId& account : accounts_set) { cookies_after_reconcile.push_back({account, true}); } } @@ -731,156 +736,165 @@ const std::vector<AccountReconcilorTestTableParam> kDiceParams = { // x: The next cookie is marked "invalid". // - First Run: true if this is the first reconcile (i.e. Chrome startup). // ------------------------------------------------------------------------- - // Tokens|Cookies|First Run|Gaia calls|Tokens aft.|Cookies aft.|M.calls| M.Tokens aft.| M.Cookies aft.| + // Tokens|Cookies|First Run|Gaia calls|Tokens aft.|Cookies aft.|M.calls| M.Tokens aft.| M.Cookies aft.| AccountReconcilorDelegate::InconsistencyReason | // ------------------------------------------------------------------------- // First reconcile (Chrome restart): Rebuild the Gaia cookie to match the // tokens. Make the Sync account the default account in the Gaia cookie. // Sync enabled. - { "*AB", "AB", IsFirstReconcile::kBoth, "", "*AB", "AB", "", "*AB", "AB"}, + { "", "A", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xA", 3}, + { "*AB", "AB", IsFirstReconcile::kBoth, "", "*AB", "AB", "", "*AB", "AB", 0}, + { "*A", "A", IsFirstReconcile::kBoth, "", "*A", "A", "", "*A" , "A", 0}, + + { "*A", "", IsFirstReconcile::kFirst, "A", "*A", "A", "UA", "*A" , "A", 1}, + { "*A", "", IsFirstReconcile::kNotFirst, "A", "*A", "A", "PA", "*A" , "A", 1}, - { "*AB", "BA", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB"}, - { "*AB", "BA", IsFirstReconcile::kNotFirst, "", "*AB", "BA", "", "*AB", "BA"}, + { "*A", "B", IsFirstReconcile::kFirst, "XA", "*A", "A", "UA", "*A" , "A", 1}, + { "*A", "B", IsFirstReconcile::kNotFirst, "XA", "*A", "A", "PA", "*A" , "xBA", 1}, - { "*AB", "A", IsFirstReconcile::kBoth, "B", "*AB", "AB", "PAB", "*AB", "AB"}, + { "*A", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "PA", "*A" , "AxB", 5}, - { "*AB", "B", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB"}, - { "*AB", "B", IsFirstReconcile::kNotFirst, "A", "*AB", "BA", "PAB", "*AB", "BA"}, + { "*AB", "BA", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB", 7}, + { "*AB", "BA", IsFirstReconcile::kNotFirst, "", "*AB", "BA", "", "*AB", "BA", 0}, - { "*AB", "", IsFirstReconcile::kFirst, "AB", "*AB", "AB", "UAB", "*AB", "AB"}, - { "*AB", "", IsFirstReconcile::kNotFirst, "AB", "*AB", "AB", "PAB", "*AB", "AB"}, + { "*AB", "A", IsFirstReconcile::kBoth, "B", "*AB", "AB", "PAB", "*AB", "AB", 4}, + + { "*AB", "B", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB", 1}, + { "*AB", "B", IsFirstReconcile::kNotFirst, "A", "*AB", "BA", "PAB", "*AB", "BA", 1}, + + { "*AB", "", IsFirstReconcile::kFirst, "AB", "*AB", "AB", "UAB", "*AB", "AB", 1}, + { "*AB", "", IsFirstReconcile::kNotFirst, "AB", "*AB", "AB", "PAB", "*AB", "AB", 1}, // Sync enabled, token error on primary. - { "*xAB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "" , "PB", "*xAB", "xAB"}, - { "*xAB", "BA", IsFirstReconcile::kBoth, "XB", "*xAB", "B", "PB", "*xAB", "BxA"}, - { "*xAB", "A", IsFirstReconcile::kBoth, "X", "*xA", "" , "PB", "*xAB", "xAB"}, - { "*xAB", "B", IsFirstReconcile::kBoth, "", "*xAB", "B", "", "*xAB", "B" }, - { "*xAB", "", IsFirstReconcile::kBoth, "B", "*xAB", "B", "PB", "*xAB", "B" }, + { "*xAB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "" , "PB", "*xAB", "xAB", 2}, + { "*xAB", "BA", IsFirstReconcile::kBoth, "XB", "*xAB", "B", "PB", "*xAB", "BxA", 2}, + { "*xAB", "A", IsFirstReconcile::kBoth, "X", "*xA", "" , "PB", "*xAB", "xAB", 2}, + { "*xAB", "B", IsFirstReconcile::kBoth, "", "*xAB", "B", "", "*xAB", "B" , 0}, + { "*xAB", "", IsFirstReconcile::kBoth, "B", "*xAB", "B", "PB", "*xAB", "B" , 0}, // Sync enabled, token error on secondary. - { "*AxB", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "PA", "*A", "AxB"}, + { "*AxB", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "PA", "*A", "AxB", 5}, - { "*AxB", "BA", IsFirstReconcile::kFirst, "XA", "*A", "A", "UA", "*A", "A" }, - { "*AxB", "BA", IsFirstReconcile::kNotFirst, "XA", "*A", "A", "PA", "*A", "xBA"}, + { "*AxB", "BA", IsFirstReconcile::kFirst, "XA", "*A", "A", "UA", "*A", "A" , 5}, + { "*AxB", "BA", IsFirstReconcile::kNotFirst, "XA", "*A", "A", "PA", "*A", "xBA", 5}, - { "*AxB", "A", IsFirstReconcile::kBoth, "", "*A", "A", "", "*A", "A" }, + { "*AxB", "A", IsFirstReconcile::kBoth, "", "*A", "A", "", "*A", "A" , 0 }, - { "*AxB", "B", IsFirstReconcile::kFirst, "XA", "*A", "A", "UA", "*A", "A" }, - { "*AxB", "B", IsFirstReconcile::kNotFirst, "XA", "*A", "A", "PA", "*A", "xBA"}, + { "*AxB", "B", IsFirstReconcile::kFirst, "XA", "*A", "A", "UA", "*A", "A" , 1}, + { "*AxB", "B", IsFirstReconcile::kNotFirst, "XA", "*A", "A", "PA", "*A", "xBA", 1 }, - { "*AxB", "", IsFirstReconcile::kFirst, "A", "*A", "A", "UA", "*A", "A" }, - { "*AxB", "", IsFirstReconcile::kNotFirst, "A", "*A", "A", "PA", "*A", "A"}, + { "*AxB", "", IsFirstReconcile::kFirst, "A", "*A", "A", "UA", "*A", "A", 1}, + { "*AxB", "", IsFirstReconcile::kNotFirst, "A", "*A", "A", "PA", "*A", "A", 1}, // Sync enabled, token error on both accounts. - { "*xAxB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xAxB"}, - { "*xAxB", "BA", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xBxA"}, - { "*xAxB", "A", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xA" }, - { "*xAxB", "B", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xB" }, - { "*xAxB", "", IsFirstReconcile::kBoth, "", "*xA", "", "", "*xA", "" }, + { "*xAxB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xAxB", 2}, + { "*xAxB", "BA", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xBxA", 2}, + { "*xAxB", "A", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xA", 2}, + { "*xAxB", "B", IsFirstReconcile::kBoth, "X", "*xA", "", "P", "*xA", "xB", 5}, + { "*xAxB", "", IsFirstReconcile::kBoth, "", "*xA", "", "", "*xA", "", 0}, // Sync disabled. - { "AB", "AB", IsFirstReconcile::kBoth, "", "AB", "AB", "", "AB", "AB"}, - { "AB", "BA", IsFirstReconcile::kBoth, "", "AB", "BA", "", "AB", "BA"}, - { "AB", "A", IsFirstReconcile::kBoth, "B", "AB", "AB", "PAB", "AB", "AB"}, - { "AB", "B", IsFirstReconcile::kBoth, "A", "AB", "BA", "PAB", "AB", "BA"}, - { "AB", "", IsFirstReconcile::kBoth, "AB", "AB", "AB", "PAB", "AB", "AB"}, + { "AB", "AB", IsFirstReconcile::kBoth, "", "AB", "AB", "", "AB", "AB", 0}, + { "AB", "BA", IsFirstReconcile::kBoth, "", "AB", "BA", "", "AB", "BA", 0}, + { "AB", "A", IsFirstReconcile::kBoth, "B", "AB", "AB", "PAB", "AB", "AB", 4}, + { "AB", "B", IsFirstReconcile::kBoth, "A", "AB", "BA", "PAB", "AB", "BA", 4}, + { "AB", "", IsFirstReconcile::kBoth, "AB", "AB", "AB", "PAB", "AB", "AB", 0}, // Sync disabled, token error on first account. - { "xAB", "AB", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAB"}, - { "xAB", "AB", IsFirstReconcile::kNotFirst, "X", "", "" , "PB", "B", "xAB"}, + { "xAB", "AB", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAB", 3}, + { "xAB", "AB", IsFirstReconcile::kNotFirst, "X", "", "" , "PB", "B", "xAB", 3}, - { "xAB", "BA", IsFirstReconcile::kBoth, "XB", "B", "B", "PB", "B", "BxA"}, + { "xAB", "BA", IsFirstReconcile::kBoth, "XB", "B", "B", "PB", "B", "BxA", 5}, - { "xAB", "A", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAB"}, - { "xAB", "A", IsFirstReconcile::kNotFirst, "X", "", "" , "PB", "B", "xAB"}, + { "xAB", "A", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAB", 3}, + { "xAB", "A", IsFirstReconcile::kNotFirst, "X", "", "" , "PB", "B", "xAB", 3}, - { "xAB", "B", IsFirstReconcile::kBoth, "", "B", "B", "", "B", "B"}, + { "xAB", "B", IsFirstReconcile::kBoth, "", "B", "B", "", "B", "B", 0}, - { "xAB", "", IsFirstReconcile::kBoth, "B", "B", "B", "PB", "B", "B"}, + { "xAB", "", IsFirstReconcile::kBoth, "B", "B", "B", "PB", "B", "B", 0}, // Sync disabled, token error on second account . - { "AxB", "AB", IsFirstReconcile::kBoth, "XA", "A", "A", "PA", "A", "AxB"}, + { "AxB", "AB", IsFirstReconcile::kBoth, "XA", "A", "A", "PA", "A", "AxB", 5}, - { "AxB", "BA", IsFirstReconcile::kFirst, "XA", "A", "A", "PA", "A", "xBA"}, - { "AxB", "BA", IsFirstReconcile::kNotFirst, "X", "", "" , "PA", "A", "xBA"}, + { "AxB", "BA", IsFirstReconcile::kFirst, "XA", "A", "A", "PA", "A", "xBA", 3}, + { "AxB", "BA", IsFirstReconcile::kNotFirst, "X", "", "" , "PA", "A", "xBA", 3}, - { "AxB", "A", IsFirstReconcile::kBoth, "", "A", "A", "", "A", "A"}, + { "AxB", "A", IsFirstReconcile::kBoth, "", "A", "A", "", "A", "A", 0}, - { "AxB", "B", IsFirstReconcile::kFirst, "XA", "A", "A", "PA", "A", "xBA"}, - { "AxB", "B", IsFirstReconcile::kNotFirst, "X", "", "" , "PA", "A", "xBA"}, + { "AxB", "B", IsFirstReconcile::kFirst, "XA", "A", "A", "PA", "A", "xBA", 3}, + { "AxB", "B", IsFirstReconcile::kNotFirst, "X", "", "" , "PA", "A", "xBA", 3}, - { "AxB", "", IsFirstReconcile::kBoth, "A", "A", "A", "PA", "A", "A"}, + { "AxB", "", IsFirstReconcile::kBoth, "A", "A", "A", "PA", "A", "A", 0}, // Sync disabled, token error on both accounts. - { "xAxB", "AB", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xAxB"}, - { "xAxB", "BA", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xBxA"}, - { "xAxB", "A", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xA"}, - { "xAxB", "B", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xB"}, - { "xAxB", "", IsFirstReconcile::kBoth, "", "", "", "", "", ""}, + { "xAxB", "AB", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xAxB", 3}, + { "xAxB", "BA", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xBxA", 3}, + { "xAxB", "A", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xA", 3}, + { "xAxB", "B", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xB", 3}, + { "xAxB", "", IsFirstReconcile::kBoth, "", "", "", "", "", "", 0}, // Account marked as invalid in cookies. // No difference between cookies and tokens, do not do do anything. // Do not logout. Regression tests for http://crbug.com/854799 - { "", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA"}, - { "", "xAxB", IsFirstReconcile::kBoth, "", "", "xAxB", "", "", "xAxB"}, - { "xA", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA"}, - { "xAB", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB"}, - { "AxB", "AxC", IsFirstReconcile::kBoth, "", "A", "AxC", "", "A", "AxC"}, - { "B", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB"}, - { "*xA", "xA", IsFirstReconcile::kBoth, "", "*xA", "xA", "", "*xA", "xA"}, - { "*xA", "xB", IsFirstReconcile::kBoth, "", "*xA", "xB", "", "*xA", "xB"}, - { "*xAB", "xAB", IsFirstReconcile::kBoth, "", "*xAB", "xAB", "", "*xAB", "xAB"}, - { "*AxB", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA"}, + { "", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA", 0}, + { "", "xAxB", IsFirstReconcile::kBoth, "", "", "xAxB", "", "", "xAxB", 0}, + { "xA", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA", 0}, + { "xAB", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB", 0}, + { "AxB", "AxC", IsFirstReconcile::kBoth, "", "A", "AxC", "", "A", "AxC", 0}, + { "B", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB", 0}, + { "*xA", "xA", IsFirstReconcile::kBoth, "", "*xA", "xA", "", "*xA", "xA", 0}, + { "*xA", "xB", IsFirstReconcile::kBoth, "", "*xA", "xB", "", "*xA", "xB", 0}, + { "*xAB", "xAB", IsFirstReconcile::kBoth, "", "*xAB", "xAB", "", "*xAB", "xAB", 0}, + { "*AxB", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA", 0}, // Appending a new cookie after the invalid one. - { "B", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB"}, - { "xAB", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB"}, + { "B", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB", 4}, + { "xAB", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB", 4}, // Refresh existing cookies. - { "AB", "xAB", IsFirstReconcile::kBoth, "A", "AB", "AB", "PAB","AB", "AB"}, - { "*AB", "xBxA", IsFirstReconcile::kNotFirst, "BA", "*AB", "BA", "PAB","*AB", "BA"}, + { "AB", "xAB", IsFirstReconcile::kBoth, "A", "AB", "AB", "PAB","AB", "AB", 4}, + { "*AB", "xBxA", IsFirstReconcile::kNotFirst, "BA", "*AB", "BA", "PAB","*AB", "BA", 1}, // Appending and invalidating cookies at the same time. // Difference should disappear after migrating to Multilogin. - { "xAB", "xAC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAxCB"}, - { "xAB", "xAC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xAxCB"}, + { "xAB", "xAC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAxCB", 6}, + { "xAB", "xAC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xAxCB", 6}, - { "xAB", "AxC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAxCB"}, - { "xAB", "AxC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xAxCB"}, + { "xAB", "AxC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xAxCB", 3}, + { "xAB", "AxC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xAxCB", 3}, - { "*xAB", "xABC", IsFirstReconcile::kFirst, "XB", "*xAB", "B", "PB", "*xAB", "xABxC"}, - { "*xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "*xA", "", "PB", "*xAB", "xABxC"}, + { "*xAB", "xABC", IsFirstReconcile::kFirst, "XB", "*xAB", "B", "PB", "*xAB", "xABxC", 5}, + { "*xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "*xA", "", "PB", "*xAB", "xABxC", 5}, - { "xAB", "xABC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xABxC"}, - { "xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xABxC"}, + { "xAB", "xABC", IsFirstReconcile::kFirst, "XB", "B", "B", "PB", "B", "xABxC", 5}, + { "xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "", "", "PB", "B", "xABxC", 5}, // Miscellaneous cases. // Check that unknown Gaia accounts are signed out. - { "", "A", IsFirstReconcile::kBoth, "X", "", "", "P", "", "xA"}, - { "*A", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "PA", "*A", "AxB"}, + { "*A", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "PA", "*A", "AxB", 5}, // Check that Gaia default account is kept in first position. - { "AB", "BC", IsFirstReconcile::kBoth, "XBA","AB", "BA", "PAB","AB", "BxCA"}, + { "AB", "BC", IsFirstReconcile::kBoth, "XBA","AB", "BA", "PAB","AB", "BxCA", 6}, // Check that Gaia cookie order is preserved for B. - { "*ABC", "CB", IsFirstReconcile::kFirst, "XABC","*ABC", "ABC", "UABC","*ABC","ABC"}, + { "*ABC", "CB", IsFirstReconcile::kFirst, "XABC","*ABC", "ABC", "UABC","*ABC","ABC", 1}, // Required for idempotency check. - { "", "", IsFirstReconcile::kNotFirst, "", "", "", "", "", ""}, - { "", "xA", IsFirstReconcile::kNotFirst, "", "", "xA", "", "", "xA"}, - { "", "xB", IsFirstReconcile::kNotFirst, "", "", "xB", "", "", "xB"}, - { "", "xAxB", IsFirstReconcile::kNotFirst, "", "", "xAxB", "", "", "xAxB"}, - { "", "xBxA", IsFirstReconcile::kNotFirst, "", "", "xBxA", "", "", "xBxA"}, - { "*A", "A", IsFirstReconcile::kNotFirst, "", "*A", "A", "", "*A", "A"}, - { "*A", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA"}, - { "*A", "AxB", IsFirstReconcile::kNotFirst, "", "*A", "AxB", "", "*A", "AxB"}, - { "A", "A", IsFirstReconcile::kNotFirst, "", "A", "A", "", "A", "A"}, - { "A", "xBA", IsFirstReconcile::kNotFirst, "", "A", "xBA", "", "A", "xBA"}, - { "A", "AxB", IsFirstReconcile::kNotFirst, "", "A", "AxB", "", "A", "AxB"}, - { "B", "B", IsFirstReconcile::kNotFirst, "", "B", "B", "", "B", "B"}, - { "B", "xAB", IsFirstReconcile::kNotFirst, "", "B", "xAB", "", "B", "xAB"}, - { "B", "BxA", IsFirstReconcile::kNotFirst, "", "B", "BxA", "", "B", "BxA"}, - { "*xA", "", IsFirstReconcile::kNotFirst, "", "*xA", "", "", "*xA", ""}, - { "*xA", "xAxB", IsFirstReconcile::kNotFirst, "", "*xA", "xAxB", "", "*xA", "xAxB"}, - { "*xA", "xBxA", IsFirstReconcile::kNotFirst, "", "*xA", "xBxA", "", "*xA", "xBxA"}, - { "*xA", "xA", IsFirstReconcile::kNotFirst, "", "*xA", "xA", "", "*xA", "xA"}, - { "*xA", "xB", IsFirstReconcile::kNotFirst, "", "*xA", "xB", "", "*xA", "xB"}, - { "*xAB", "B", IsFirstReconcile::kNotFirst, "", "*xAB", "B", "", "*xAB", "B"}, - { "*xAB", "BxA", IsFirstReconcile::kNotFirst, "", "*xAB", "BxA", "", "*xAB", "BxA"}, - { "*xAB", "xAB", IsFirstReconcile::kNotFirst, "", "*xAB", "xAB", "", "*xAB", "xAB"}, - { "*xAB", "xABxC",IsFirstReconcile::kNotFirst, "", "*xAB", "xABxC","", "*xAB", "xABxC"}, - { "A", "AxC", IsFirstReconcile::kNotFirst, "", "A", "AxC", "", "A", "AxC"}, - { "AB", "BxCA", IsFirstReconcile::kNotFirst, "", "AB", "BxCA", "", "AB", "BxCA"}, - { "B", "xABxC",IsFirstReconcile::kNotFirst, "", "B", "xABxC","", "B", "xABxC"}, - { "B", "xAxCB",IsFirstReconcile::kNotFirst, "", "B", "xAxCB","", "B", "xAxCB"}, - { "*ABC", "ACB", IsFirstReconcile::kNotFirst, "", "*ABC", "ACB", "", "*ABC", "ACB"}, - { "*ABC", "ABC", IsFirstReconcile::kNotFirst, "", "*ABC", "ABC", "", "*ABC", "ABC"} + { "", "", IsFirstReconcile::kNotFirst, "", "", "", "", "", "", 0}, + { "", "xA", IsFirstReconcile::kNotFirst, "", "", "xA", "", "", "xA", 0}, + { "", "xB", IsFirstReconcile::kNotFirst, "", "", "xB", "", "", "xB", 0}, + { "", "xAxB", IsFirstReconcile::kNotFirst, "", "", "xAxB", "", "", "xAxB", 0}, + { "", "xBxA", IsFirstReconcile::kNotFirst, "", "", "xBxA", "", "", "xBxA", 0}, + { "*A", "A", IsFirstReconcile::kNotFirst, "", "*A", "A", "", "*A", "A", 0}, + { "*A", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA", 0}, + { "*A", "AxB", IsFirstReconcile::kNotFirst, "", "*A", "AxB", "", "*A", "AxB", 0}, + { "A", "A", IsFirstReconcile::kNotFirst, "", "A", "A", "", "A", "A", 0}, + { "A", "xBA", IsFirstReconcile::kNotFirst, "", "A", "xBA", "", "A", "xBA", 0}, + { "A", "AxB", IsFirstReconcile::kNotFirst, "", "A", "AxB", "", "A", "AxB", 0}, + { "B", "B", IsFirstReconcile::kNotFirst, "", "B", "B", "", "B", "B", 0}, + { "B", "xAB", IsFirstReconcile::kNotFirst, "", "B", "xAB", "", "B", "xAB", 0}, + { "B", "BxA", IsFirstReconcile::kNotFirst, "", "B", "BxA", "", "B", "BxA", 0}, + { "*xA", "", IsFirstReconcile::kNotFirst, "", "*xA", "", "", "*xA", "", 0}, + { "*xA", "xAxB", IsFirstReconcile::kNotFirst, "", "*xA", "xAxB", "", "*xA", "xAxB", 0}, + { "*xA", "xBxA", IsFirstReconcile::kNotFirst, "", "*xA", "xBxA", "", "*xA", "xBxA", 0}, + { "*xA", "xA", IsFirstReconcile::kNotFirst, "", "*xA", "xA", "", "*xA", "xA", 0}, + { "*xA", "xB", IsFirstReconcile::kNotFirst, "", "*xA", "xB", "", "*xA", "xB", 0}, + { "*xAB", "B", IsFirstReconcile::kNotFirst, "", "*xAB", "B", "", "*xAB", "B", 0}, + { "*xAB", "BxA", IsFirstReconcile::kNotFirst, "", "*xAB", "BxA", "", "*xAB", "BxA", 0}, + { "*xAB", "xAB", IsFirstReconcile::kNotFirst, "", "*xAB", "xAB", "", "*xAB", "xAB", 0}, + { "*xAB", "xABxC",IsFirstReconcile::kNotFirst, "", "*xAB", "xABxC","", "*xAB", "xABxC", 0}, + { "A", "AxC", IsFirstReconcile::kNotFirst, "", "A", "AxC", "", "A", "AxC", 0}, + { "AB", "BxCA", IsFirstReconcile::kNotFirst, "", "AB", "BxCA", "", "AB", "BxCA", 0}, + { "B", "xABxC",IsFirstReconcile::kNotFirst, "", "B", "xABxC","", "B", "xABxC", 0}, + { "B", "xAxCB",IsFirstReconcile::kNotFirst, "", "B", "xAxCB","", "B", "xAxCB", 0}, + { "*ABC", "ACB", IsFirstReconcile::kNotFirst, "", "*ABC", "ACB", "", "*ABC", "ACB", 0}, + { "*ABC", "ABC", IsFirstReconcile::kNotFirst, "", "*ABC", "ABC", "", "*ABC", "ABC", 0} }; // clang-format on @@ -893,13 +907,14 @@ TEST_P(AccountReconcilorTestTable, TableRowTest) { // nothing on the second call. CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/false); - // Setup tokens. - SetupTokens(); - // 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(); + // Call list accounts now so that the next call completes synchronously. identity_test_env()->identity_manager()->GetAccountsInCookieJar(); base::RunLoop().RunUntilIdle(); @@ -916,7 +931,8 @@ TEST_P(AccountReconcilorTestTable, TableRowTest) { continue; } std::string cookie(1, GetParam().gaia_api_calls[i]); - std::string account_id_for_cookie = GaiaIdForAccountKey(cookie[0]); + CoreAccountId account_id_for_cookie = PickAccountIdForAccount( + accounts_[cookie[0]].gaia_id, accounts_[cookie[0]].email); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_for_cookie)) .Times(1); // MergeSession fixes an existing cookie or appends it at the end. @@ -941,12 +957,12 @@ TEST_P(AccountReconcilorTestTable, TableRowTest) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor->first_execution_); reconcilor->first_execution_ = - GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; + GetParam().is_first_reconcile == IsFirstReconcile::kFirst; reconcilor->StartReconcile(); for (int i = 0; GetParam().gaia_api_calls[i] != '\0'; ++i) { if (GetParam().gaia_api_calls[i] == 'X') continue; - std::string account_id = + CoreAccountId account_id = PickAccountIdForAccount(accounts_[GetParam().gaia_api_calls[i]].gaia_id, accounts_[GetParam().gaia_api_calls[i]].email); SimulateAddAccountToCookieCompleted( @@ -977,6 +993,44 @@ TEST_P(AccountReconcilorTestTable, TableRowTest) { base::RunLoop().RunUntilIdle(); } +// Checks one row of the kDiceParams table above. +TEST_P(AccountReconcilorTestTable, InconsistencyReasonLogging) { + // Enable Dice Migration. + SetAccountConsistency(signin::AccountConsistencyMethod::kDiceMigration); + // 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(); + // Call list accounts now so that the next call completes synchronously. + identity_test_env()->identity_manager()->GetAccountsInCookieJar(); + base::RunLoop().RunUntilIdle(); + + EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)) + .WillRepeatedly(testing::Return()); + EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) + .WillRepeatedly(testing::Return()); + // Reconcile. + AccountReconcilor* reconcilor = GetMockReconcilor(); + bool first_execution = + GetParam().is_first_reconcile == IsFirstReconcile::kFirst; + reconcilor->first_execution_ = first_execution; + reconcilor->StartReconcile(); + reconcilor->add_to_cookie_.clear(); + reconcilor->CalculateIfReconcileIsDone(); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + std::string histogram_name_suffix = + first_execution ? "FirstExecution" : "NotFirstExecution"; + + histogram_tester()->ExpectUniqueSample( + "Signin.DiceMigrationNotReady.Reason." + histogram_name_suffix, + GetParam().inconsistency_reason, 1); + + ConfigureCookieManagerService({}); + base::RunLoop().RunUntilIdle(); +} + INSTANTIATE_TEST_SUITE_P( DiceTable, AccountReconcilorTestTable, @@ -1003,14 +1057,15 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/true); - // Setup tokens. - SetupTokens(); - // Setup cookies. std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies); ConfigureCookieManagerService(cookies); std::vector<Cookie> cookies_after_reconcile = cookies; + // Setup tokens. This triggers listing cookies so we need to setup cookies + // before that. + SetupTokens(); + // Call list accounts now so that the next call completes synchronously. identity_test_env()->identity_manager()->GetAccountsInCookieJar(); base::RunLoop().RunUntilIdle(); @@ -1024,7 +1079,7 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { : gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER; // Generate expected array of accounts in cookies and set fake gaia // response. - std::vector<std::string> accounts_to_send; + std::vector<CoreAccountId> accounts_to_send; for (int i = 1; GetParam().gaia_api_calls_multilogin[i] != '\0'; ++i) { accounts_to_send.push_back( accounts_[GetParam().gaia_api_calls_multilogin[i]].gaia_id); @@ -1114,15 +1169,17 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceTokenServiceRegistration) { // Tests that reconcile starts even when Sync is not enabled. TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) { - // Add a token in Chrome but do not sign in. - const std::string account_id = - identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id; + // Add a token in Chrome but do not sign in. Making account available (setting + // a refresh token) triggers listing cookies so we need to setup cookies + // before that. signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); + const CoreAccountId account_id = + identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id; if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -1166,13 +1223,18 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileNoop) { // Tests that the first Gaia account is re-used when possible. TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileReuseGaiaFirstAccount) { - // Add accounts 1 and 2 to the token service. + // Add account "other" to the Gaia cookie. + signin::SetListAccountsResponseTwoAccounts( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + "foo@gmail.com", "9999", &test_url_loader_factory_); + + // Add accounts "user" and "other" to the token service. const AccountInfo account_info_1 = identity_test_env()->MakeAccountAvailable("user@gmail.com"); - const std::string account_id_1 = account_info_1.account_id; + const CoreAccountId account_id_1 = account_info_1.account_id; const AccountInfo account_info_2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id_2 = account_info_2.account_id; + const CoreAccountId account_id_2 = account_info_2.account_id; auto* identity_manager = identity_test_env()->identity_manager(); std::vector<CoreAccountInfo> accounts = @@ -1181,11 +1243,6 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_2)); - // Add account 2 to the Gaia cookie. - signin::SetListAccountsResponseTwoAccounts( - account_info_2.email, account_info_2.gaia, "foo@gmail.com", "9999", - &test_url_loader_factory_); - if (!IsMultiloginEnabled()) { testing::InSequence mock_sequence; EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()); @@ -1193,7 +1250,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_2)); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1)); } else { - std::vector<std::string> accounts_to_send = {account_id_2, account_id_1}; + std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1}; // Send accounts to Gaia in any order, it will determine the order itself in // PRESERVE order. const signin::MultiloginParameters params( @@ -1223,20 +1280,25 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, // lost. TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { // Add accounts to the token service and the Gaia cookie in a different order. + // Making account available (setting a refresh token) triggers listing cookies + // so we need to setup cookies before that. + signin::SetListAccountsResponseTwoAccounts( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + "user@gmail.com", signin::GetTestGaiaIdForEmail("user@gmail.com"), + &test_url_loader_factory_); + AccountInfo account_info_1 = identity_test_env()->MakeAccountAvailable("user@gmail.com"); - const std::string account_id_1 = account_info_1.account_id; + const CoreAccountId account_id_1 = account_info_1.account_id; AccountInfo account_info_2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id_2 = account_info_2.account_id; - signin::SetListAccountsResponseTwoAccounts( - account_info_2.email, account_info_2.gaia, account_info_1.email, - account_info_1.gaia, &test_url_loader_factory_); + const CoreAccountId account_id_2 = account_info_2.account_id; auto* identity_manager = identity_test_env()->identity_manager(); std::vector<CoreAccountInfo> accounts = identity_manager->GetAccountsWithRefreshTokens(); ASSERT_EQ(2u, accounts.size()); + ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_2)); @@ -1274,7 +1336,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { } else { // Since Gaia can't know about cached account, make sure that we reorder // chrome accounts accordingly even in PRESERVE mode. - std::vector<std::string> accounts_to_send = {account_id_2, account_id_1}; + std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -1330,7 +1392,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { &test_url_loader_factory_); // Add a token to Chrome. - const std::string chrome_account_id = + const CoreAccountId chrome_account_id = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; if (!IsMultiloginEnabled()) { @@ -1343,7 +1405,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { } else { // In PRESERVE mode it is up to Gaia to not delete existing accounts in // cookies and not sign out unveridied accounts. - std::vector<std::string> accounts_to_send = {chrome_account_id}; + std::vector<CoreAccountId> accounts_to_send = {chrome_account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -1371,11 +1433,14 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceMigrationAfterNoop) { SetAccountConsistency(signin::AccountConsistencyMethod::kDiceMigration); pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true); - // Chrome account is consistent with the cookie. + // Chrome account is consistent with the cookie. Making account available + // (setting a refresh token) triggers listing cookies so we need to setup + // cookies before that. + signin::SetListAccountsResponseOneAccount( + "user@gmail.com", signin::GetTestGaiaIdForEmail("user@gmail.com"), + &test_url_loader_factory_); AccountInfo account_info = identity_test_env()->MakeAccountAvailable("user@gmail.com"); - signin::SetListAccountsResponseOneAccount( - account_info.email, account_info.gaia, &test_url_loader_factory_); AccountReconcilor* reconcilor = GetMockReconcilor(); // Dice is not enabled by default. EXPECT_FALSE(reconcilor->delegate_->IsAccountConsistencyEnforced()); @@ -1401,11 +1466,14 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, // Enable Dice migration. SetAccountConsistency(signin::AccountConsistencyMethod::kDiceMigration); - // Chrome account is consistent with the cookie. + // Chrome account is consistent with the cookie. Making account available + // (setting a refresh token) triggers listing cookies so we need to setup + // cookies before that. + signin::SetListAccountsResponseOneAccount( + "user@gmail.com", signin::GetTestGaiaIdForEmail("user@gmail.com"), + &test_url_loader_factory_); AccountInfo account_info = identity_test_env()->MakeAccountAvailable("user@gmail.com"); - signin::SetListAccountsResponseOneAccount( - account_info.email, account_info.gaia, &test_url_loader_factory_); AccountReconcilor* reconcilor = GetMockReconcilor(); // Dice is not enabled by default. EXPECT_FALSE(reconcilor->delegate_->IsAccountConsistencyEnforced()); @@ -1432,7 +1500,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceNoMigrationAfterReconcile) { pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true); // Add a token in Chrome. - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); AccountReconcilor* reconcilor = GetMockReconcilor(); @@ -1472,9 +1540,9 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, MigrationClearSecondaryTokens) { pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true); // Add a tokens in Chrome, signin to Sync, but no Gaia cookies. - const std::string account_id_1 = + const CoreAccountId account_id_1 = ConnectProfileToAccount("user@gmail.com").account_id; - const std::string account_id_2 = + const CoreAccountId account_id_2 = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); @@ -1487,7 +1555,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, MigrationClearSecondaryTokens) { if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1)); } else { - std::vector<std::string> accounts_to_send = {account_id_1}; + std::vector<CoreAccountId> accounts_to_send = {account_id_1}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -1522,12 +1590,14 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, MigrationClearAllTokens) { SetAccountConsistency(signin::AccountConsistencyMethod::kDiceMigration); pref_service()->SetBoolean(prefs::kTokenServiceDiceCompatible, true); - // Add a tokens in Chrome but no Gaia cookies. - const std::string account_id_1 = + // Add a tokens in Chrome but no Gaia cookies. Making account available + // (setting a refresh token) triggers listing cookies so we need to setup + // cookies before that. + signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); + const CoreAccountId account_id_1 = identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id; - const std::string account_id_2 = + const CoreAccountId account_id_2 = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; - signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); auto* identity_manager = identity_test_env()->identity_manager(); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); @@ -1556,11 +1626,11 @@ INSTANTIATE_TEST_SUITE_P(TestDiceEndpoint, TEST_F(AccountReconcilorTest, DiceDeleteCookie) { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); - const std::string primary_account_id = + const CoreAccountId primary_account_id = identity_test_env() ->MakePrimaryAccountAvailable("user@gmail.com") .account_id; - const std::string secondary_account_id = + const CoreAccountId secondary_account_id = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; auto* identity_manager = identity_test_env()->identity_manager(); @@ -1611,7 +1681,7 @@ TEST_F(AccountReconcilorTest, DiceDeleteCookie) { EXPECT_FALSE( identity_manager->HasAccountWithRefreshTokenInPersistentErrorState( primary_account_id)); - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), primary_account_id, GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: @@ -1707,7 +1777,7 @@ TEST_P(AccountReconcilorTestMirrorMultilogin, TableRowTest) { continue; } if (GetParam().gaia_api_calls[i] == 'U') { - std::vector<std::string> accounts_to_send; + 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); @@ -1801,13 +1871,14 @@ TEST_P(AccountReconcilorTestMiceMultilogin, TableRowTest) { scoped_feature_list_.InitWithFeatures( {kUseMultiloginEndpoint, signin::kMiceFeature}, {}); - // Setup tokens. - SetupTokens(); - // 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(); + // Call list accounts now so that the next call completes synchronously. identity_test_env()->identity_manager()->GetAccountsInCookieJar(); base::RunLoop().RunUntilIdle(); @@ -1824,7 +1895,7 @@ TEST_P(AccountReconcilorTestMiceMultilogin, TableRowTest) { continue; } if (GetParam().gaia_api_calls[i] == 'U') { - std::vector<std::string> accounts_to_send; + 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); @@ -1949,7 +2020,7 @@ TEST_F(AccountReconcilorMiceTest, AccountReconcilorStateScheduled) { // Tests that reconcile cannot start before the tokens are loaded, and is // automatically started when tokens are loaded. TEST_P(AccountReconcilorMirrorEndpointParamTest, TokensNotLoaded) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState(); @@ -1957,18 +2028,15 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TokensNotLoaded) { AccountReconcilor* reconcilor = GetMockReconcilor(); reconcilor->StartReconcile(); -#if !defined(OS_CHROMEOS) - // No reconcile when tokens are not loaded, except on ChromeOS where reconcile - // can start as long as the token service is not empty. + // No reconcile when tokens are not loaded. ASSERT_FALSE(reconcilor->is_reconcile_started_); // When tokens are loaded, reconcile starts automatically. identity_test_env()->ReloadAccountsFromDisk(); -#endif if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -1989,7 +2057,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TokensNotLoaded) { TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieSuccess) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; signin::SetListAccountsResponseOneAccountWithParams( {account_info.email, account_info.gaia, false /* valid */, false /* signed_out */, true /* verified */}, @@ -1998,7 +2066,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieSuccess) { if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2015,7 +2083,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieSuccess) { base::RunLoop().RunUntilIdle(); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = + 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()); @@ -2025,7 +2093,8 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieSuccess) { TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieFailure) { ConnectProfileToAccount("user@gmail.com"); - signin::SetListAccountsResponseWebLoginRequired(&test_url_loader_factory_); + signin::SetListAccountsResponseWithUnexpectedServiceResponse( + &test_url_loader_factory_); AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); @@ -2036,11 +2105,14 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieFailure) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); base::RunLoop().RunUntilIdle(); - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = identity_test_env()->identity_manager()->GetAccountsInCookieJar(); ASSERT_FALSE(accounts_in_cookie_jar_info.accounts_are_fresh); ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_in_accounts.size()); ASSERT_EQ(0u, accounts_in_cookie_jar_info.signed_out_accounts.size()); + // List accounts retries once on |UNEXPECTED_SERVICE_RESPONSE| errors with + // backoff protection. + task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(2)); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_ERROR, reconcilor->GetState()); } @@ -2048,7 +2120,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, GetAccountsFromCookieFailure) { TEST_P(AccountReconcilorMirrorEndpointParamTest, ExtraCookieChangeNotification) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; signin::CookieParams cookie_params = { account_info.email, account_info.gaia, false /* valid */, false /* signed_out */, true /* verified */}; @@ -2059,7 +2131,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2077,7 +2149,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, // Add extra cookie change notification. Reconcilor should ignore it. gaia::ListedAccount listed_account = ListedAccountFromCookieParams(cookie_params, account_id); - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = { /*accounts_are_fresh=*/true, {listed_account}, {}}; reconcilor->OnAccountsInCookieUpdated( accounts_in_cookie_jar_info, GoogleServiceAuthError::AuthErrorNone()); @@ -2121,7 +2193,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileNoop) { TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileCookiesDisabled) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); test_signin_client()->set_are_signin_cookies_allowed(false); @@ -2135,7 +2207,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, base::RunLoop().RunUntilIdle(); std::vector<gaia::ListedAccount> accounts; // This will be the first call to ListAccounts. - identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info = + signin::AccountsInCookieJarInfo accounts_in_cookie_jar_info = identity_test_env()->identity_manager()->GetAccountsInCookieJar(); ASSERT_FALSE(accounts_in_cookie_jar_info.accounts_are_fresh); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2143,7 +2215,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileContentSettings) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); @@ -2163,7 +2235,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileContentSettingsGaiaUrl) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); @@ -2178,7 +2250,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileContentSettingsNonGaiaUrl) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); @@ -2193,7 +2265,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileContentSettingsInvalidPattern) { - const std::string account_id = + const CoreAccountId account_id = ConnectProfileToAccount("user@gmail.com").account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); @@ -2209,17 +2281,16 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, } // This test is needed until chrome changes to use gaia obfuscated id. -// The signin manager and token service use the gaia "email" property, which -// preserves dots in usernames and preserves case. gaia::ParseListAccountsData() -// however uses gaia "displayEmail" which does not preserve case, and then -// passes the string through gaia::CanonicalizeEmail() which removes dots. This -// tests makes sure that an email like "Dot.S@hmail.com", as seen by the -// token service, will be considered the same as "dots@gmail.com" as returned -// by gaia::ParseListAccountsData(). +// The primary account manager and token service use the gaia "email" property, +// which preserves dots in usernames and preserves case. +// gaia::ParseListAccountsData() however uses gaia "displayEmail" which does not +// preserve case, and then passes the string through gaia::CanonicalizeEmail() +// which removes dots. This tests makes sure that an email like +// "Dot.S@hmail.com", as seen by the token service, will be considered the same +// as "dots@gmail.com" as returned by gaia::ParseListAccountsData(). TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileNoopWithDots) { if (identity_test_env()->identity_manager()->GetAccountIdMigrationState() != - identity::IdentityManager::AccountIdMigrationState:: - MIGRATION_NOT_STARTED) { + signin::IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED) { return; } @@ -2266,18 +2337,18 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileNoopMultiple) { TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileAddToCookie) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); signin::SetListAccountsResponseOneAccount( account_info.email, account_info.gaia, &test_url_loader_factory_); - const std::string account_id2 = + const CoreAccountId account_id2 = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); } else { - std::vector<std::string> accounts_to_send = {account_id, account_id2}; + std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2316,10 +2387,10 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileAddToCookie) { } TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { - class TestGaiaCookieObserver : public identity::IdentityManager::Observer { + class TestGaiaCookieObserver : public signin::IdentityManager::Observer { public: void OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) override { cookies_updated_ = true; } @@ -2339,7 +2410,7 @@ TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { // Add one account to Chrome and instantiate the reconcilor. AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); TestGaiaCookieObserver observer; identity_test_env()->identity_manager()->AddObserver(&observer); @@ -2355,7 +2426,7 @@ TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { // Set an authentication error. ASSERT_FALSE(observer.cookies_updated_); - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), account_id, GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: @@ -2376,18 +2447,18 @@ TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { TEST_P(AccountReconcilorMirrorEndpointParamTest, SignoutAfterErrorDoesNotRecordUma) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); signin::SetListAccountsResponseOneAccount( account_info.email, account_info.gaia, &test_url_loader_factory_); - const std::string account_id2 = + const CoreAccountId account_id2 = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); } else { - std::vector<std::string> accounts_to_send = {account_id, account_id2}; + std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2427,7 +2498,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileRemoveFromCookie) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; identity_test_env()->SetRefreshTokenForAccount(account_id); signin::SetListAccountsResponseTwoAccounts( account_info.email, account_info.gaia, "other@gmail.com", "12345", @@ -2437,7 +2508,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2473,7 +2544,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, // Check that reconcile is aborted if there is token error on primary account. TEST_P(AccountReconcilorMirrorEndpointParamTest, TokenErrorOnPrimary) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), account_info.account_id, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); @@ -2491,14 +2562,14 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TokenErrorOnPrimary) { TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileAddToCookieTwice) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; AccountInfo account_info2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id2 = account_info2.account_id; + const CoreAccountId account_id2 = account_info2.account_id; const std::string email3 = "third@gmail.com"; - const std::string gaia_id3 = identity::GetTestGaiaIdForEmail(email3); - const std::string account_id3 = PickAccountIdForAccount(gaia_id3, email3); + const std::string gaia_id3 = signin::GetTestGaiaIdForEmail(email3); + const CoreAccountId account_id3 = PickAccountIdForAccount(gaia_id3, email3); signin::SetListAccountsResponseOneAccount( account_info.email, account_info.gaia, &test_url_loader_factory_); @@ -2507,7 +2578,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id3)); } else { - std::vector<std::string> accounts_to_send = {account_id, account_id2}; + std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2547,8 +2618,8 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, // This will cause the reconcilor to fire. identity_test_env()->MakeAccountAvailable(email3); if (IsMultiloginEnabled()) { - std::vector<std::string> accounts_to_send = {account_id, account_id2, - account_id3}; + std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2, + account_id3}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2586,11 +2657,11 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileBadPrimary) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; AccountInfo account_info2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id2 = account_info2.account_id; + const CoreAccountId account_id2 = account_info2.account_id; signin::SetListAccountsResponseTwoAccounts( account_info2.email, account_info2.gaia, account_info.email, account_info.gaia, &test_url_loader_factory_); @@ -2600,7 +2671,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileBadPrimary) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); } else { - std::vector<std::string> accounts_to_send = {account_id, account_id2}; + std::vector<CoreAccountId> accounts_to_send = {account_id, account_id2}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2731,10 +2802,10 @@ TEST_P(AccountReconcilorMethodParamTest, StartReconcileWithSessionInfoExpiredDefault) { SetAccountConsistency(GetParam()); AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; AccountInfo account_info2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id2 = account_info2.account_id; + const CoreAccountId account_id2 = account_info2.account_id; signin::SetListAccountsResponseWithParams( {{account_info.email, account_info.gaia, false /* valid */, false /* signed_out */, true /* verified */}, @@ -2774,7 +2845,7 @@ TEST_P(AccountReconcilorMethodParamTest, AccountReconcilorStateScheduled) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); AccountInfo account_info2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id2 = account_info2.account_id; + const CoreAccountId account_id2 = account_info2.account_id; signin::SetListAccountsResponseOneAccount( account_info.email, account_info.gaia, &test_url_loader_factory_); @@ -2837,7 +2908,7 @@ TEST_P(AccountReconcilorMethodParamTest, AccountReconcilorStateScheduled) { TEST_P(AccountReconcilorMirrorEndpointParamTest, AddAccountToCookieCompletedWithBogusAccount) { AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id = account_info.account_id; + const CoreAccountId account_id = account_info.account_id; signin::SetListAccountsResponseOneAccountWithParams( {account_info.email, account_info.gaia, false /* valid */, false /* signed_out */, true /* verified */}, @@ -2846,7 +2917,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); } else { - std::vector<std::string> accounts_to_send = {account_id}; + std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2877,17 +2948,17 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TEST_P(AccountReconcilorMirrorEndpointParamTest, NoLoopWithBadPrimary) { // Connect profile to a primary account and then add a secondary account. AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); - const std::string account_id1 = account_info.account_id; + const CoreAccountId account_id1 = account_info.account_id; AccountInfo account_info2 = identity_test_env()->MakeAccountAvailable("other@gmail.com"); - const std::string account_id2 = account_info2.account_id; + const CoreAccountId account_id2 = account_info2.account_id; if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id1)); EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id2)); } else { - std::vector<std::string> accounts_to_send = {account_id1, account_id2}; + std::vector<CoreAccountId> accounts_to_send = {account_id1, account_id2}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -2926,7 +2997,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, NoLoopWithBadPrimary) { // Now that we've tried once, the token service knows that the primary // account has an auth error. - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), account_id1, error); // A second attempt to reconcile should be a noop. @@ -2938,13 +3009,13 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, NoLoopWithBadPrimary) { TEST_P(AccountReconcilorMirrorEndpointParamTest, WontMergeAccountsWithError) { // Connect profile to a primary account and then add a secondary account. - const std::string account_id1 = + const CoreAccountId account_id1 = ConnectProfileToAccount("user@gmail.com").account_id; - const std::string account_id2 = + const CoreAccountId account_id2 = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; // Mark the secondary account in auth error state. - identity::UpdatePersistentErrorOfRefreshTokenForAccount( + signin::UpdatePersistentErrorOfRefreshTokenForAccount( identity_test_env()->identity_manager(), account_id2, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); @@ -2957,7 +3028,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, WontMergeAccountsWithError) { if (!IsMultiloginEnabled()) { EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id1)); } else { - std::vector<std::string> accounts_to_send = {account_id1}; + std::vector<CoreAccountId> accounts_to_send = {account_id1}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); @@ -3073,16 +3144,16 @@ TEST_F(AccountReconcilorTest, MultiloginLogout) { class MultiloginLogoutDelegate : public signin::AccountReconcilorDelegate { bool IsReconcileEnabled() const override { return true; } bool IsAccountConsistencyEnforced() const override { return true; } - std::vector<std::string> GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + 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 { return {}; } gaia::MultiloginMode CalculateModeForReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const override { return gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER; diff --git a/chromium/components/signin/core/browser/android/BUILD.gn b/chromium/components/signin/core/browser/android/BUILD.gn index 52d54b20fdb..faf53256b5f 100644 --- a/chromium/components/signin/core/browser/android/BUILD.gn +++ b/chromium/components/signin/core/browser/android/BUILD.gn @@ -11,7 +11,6 @@ generate_jni("jni_headers") { "java/src/org/chromium/components/signin/ConsistencyCookieManager.java", "java/src/org/chromium/components/signin/OAuth2TokenService.java", ] - jni_package = "components/signin" } android_library("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 index 192ea3184e8..a56464df0ff 100644 --- 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 @@ -55,7 +55,7 @@ public class AccountIdProvider { */ public boolean canBeUsed() { // TODO(http://crbug.com/577190): Remove StrictMode override. - try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) { + try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( ContextUtils.getApplicationContext()); return resultCode == ConnectionResult.SUCCESS; diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java index c05c650293f..375737023a8 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java @@ -11,7 +11,6 @@ import android.text.TextUtils; 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.VisibleForTesting; @@ -60,21 +59,10 @@ public final class OAuth2TokenService void onGetTokenFailure(boolean isTransientError); } - /** - * Classes that want to listen for refresh token availability should - * implement this interface and register with {@link #addObserver}. - */ - public interface OAuth2TokenServiceObserver { - void onRefreshTokenAvailable(Account account); - void onRefreshTokenRevoked(Account account); - void onRefreshTokensLoaded(); - } - private static final String OAUTH2_SCOPE_PREFIX = "oauth2:"; private final long mNativeOAuth2TokenServiceDelegate; private final AccountTrackerService mAccountTrackerService; - private final ObserverList<OAuth2TokenServiceObserver> mObservers = new ObserverList<>(); private boolean mPendingUpdate; @@ -93,18 +81,6 @@ public final class OAuth2TokenService return new OAuth2TokenService(nativeOAuth2TokenServiceDelegate, accountTrackerService); } - @VisibleForTesting - public void addObserver(OAuth2TokenServiceObserver observer) { - ThreadUtils.assertOnUiThread(); - mObservers.addObserver(observer); - } - - @VisibleForTesting - public void removeObserver(OAuth2TokenServiceObserver observer) { - ThreadUtils.assertOnUiThread(); - mObservers.removeObserver(observer); - } - private static Account getAccountOrNullFromUsername(String username) { if (username == null) { Log.e(TAG, "Username is null"); @@ -128,7 +104,7 @@ public final class OAuth2TokenService public static 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. - try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { List<String> accountNames = AccountManagerFacade.get().tryGetGoogleAccountNames(); return accountNames.toArray(new String[accountNames.size()]); } @@ -265,7 +241,7 @@ 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. - try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { return AccountManagerFacade.get().hasAccountForName(accountName); } } @@ -316,31 +292,6 @@ public final class OAuth2TokenService return true; } - @CalledByNative - private void notifyRefreshTokenAvailable(String accountName) { - assert accountName != null; - Account account = AccountManagerFacade.createAccountFromName(accountName); - for (OAuth2TokenServiceObserver observer : mObservers) { - observer.onRefreshTokenAvailable(account); - } - } - - @CalledByNative - public void notifyRefreshTokenRevoked(String accountName) { - assert accountName != null; - Account account = AccountManagerFacade.createAccountFromName(accountName); - for (OAuth2TokenServiceObserver observer : mObservers) { - observer.onRefreshTokenRevoked(account); - } - } - - @CalledByNative - public void notifyRefreshTokensLoaded() { - for (OAuth2TokenServiceObserver observer : mObservers) { - observer.onRefreshTokensLoaded(); - } - } - private static String[] getStoredAccounts() { Set<String> accounts = ContextUtils.getAppSharedPreferences().getStringSet(STORED_ACCOUNTS_KEY, null); 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 142c31bef94..6909815553f 100644 --- a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc +++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc @@ -53,7 +53,7 @@ GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) { ChromeConnectedHeaderHelper::ChromeConnectedHeaderHelper( AccountConsistencyMethod account_consistency) - : SigninHeaderHelper("Mirror"), account_consistency_(account_consistency) {} + : account_consistency_(account_consistency) {} // static std::string ChromeConnectedHeaderHelper::BuildRequestCookieIfPossible( diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.h b/chromium/components/signin/core/browser/chrome_connected_header_helper.h index 606056243b2..81831b2b392 100644 --- a/chromium/components/signin/core/browser/chrome_connected_header_helper.h +++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.h @@ -7,8 +7,8 @@ #include <string> -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/signin_header_helper.h" +#include "components/signin/public/base/account_consistency_method.h" class GURL; diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc index 97f4ae6f5f6..5b044244d8f 100644 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc +++ b/chromium/components/signin/core/browser/consistency_cookie_manager_android.cc @@ -4,13 +4,13 @@ #include "components/signin/core/browser/consistency_cookie_manager_android.h" -#include "jni/ConsistencyCookieManager_jni.h" -#include "services/identity/public/cpp/identity_manager.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( - identity::IdentityManager* identity_manager, + IdentityManager* identity_manager, SigninClient* signin_client, AccountReconcilor* reconcilor) : ConsistencyCookieManagerBase(signin_client, reconcilor) { diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_android.h b/chromium/components/signin/core/browser/consistency_cookie_manager_android.h index ab2b7af1d6b..093b8898262 100644 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_android.h +++ b/chromium/components/signin/core/browser/consistency_cookie_manager_android.h @@ -9,19 +9,17 @@ #include "base/macros.h" #include "components/signin/core/browser/consistency_cookie_manager_base.h" -namespace identity { -class IdentityManager; -} - 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(identity::IdentityManager* identity_manager, + ConsistencyCookieManagerAndroid(IdentityManager* identity_manager, SigninClient* signin_client, AccountReconcilor* reconcilor); diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc index c428f6e06c0..92dee2a07bc 100644 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc +++ b/chromium/components/signin/core/browser/consistency_cookie_manager_base.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "base/time/time.h" -#include "components/signin/core/browser/signin_client.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" @@ -70,9 +70,13 @@ void ConsistencyCookieManagerBase::UpdateCookie() { kCookieName, cookie_value, GaiaUrls::GetInstance()->gaia_url().host(), /*path=*/"/", /*creation=*/now, /*expiration=*/expiry, /*last_access=*/now, /*secure=*/true, /*httponly=*/false, - net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT); + 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", net::CookieOptions(), + cookie, "https", cookie_options, network::mojom::CookieManager::SetCanonicalCookieCallback()); } diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_base.h b/chromium/components/signin/core/browser/consistency_cookie_manager_base.h index 634086956e9..7c1d551c4c0 100644 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_base.h +++ b/chromium/components/signin/core/browser/consistency_cookie_manager_base.h @@ -8,7 +8,7 @@ #include "base/macros.h" #include "base/scoped_observer.h" #include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/core/browser/signin_metrics.h" +#include "components/signin/public/base/signin_metrics.h" class SigninClient; diff --git a/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc b/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc index 168581b1bac..5023f2c213f 100644 --- a/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc +++ b/chromium/components/signin/core/browser/consistency_cookie_manager_unittest.cc @@ -9,13 +9,13 @@ #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_reconcilor.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "components/signin/core/browser/test_signin_client.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/identity/public/cpp/identity_test_environment.h" #include "services/network/test/test_cookie_manager.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,12 +25,22 @@ 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(), - net::CookieOptions()) == + cookie_options) == net::CanonicalCookie::CookieInclusionStatus::INCLUDE; } +MATCHER(SetPermittedInContext, "") { + const net::CanonicalCookie& cookie = testing::get<0>(arg); + const net::CookieOptions& cookie_options = testing::get<1>(arg); + return cookie.IsSetPermittedInContext(cookie_options) == + net::CanonicalCookie::CookieInclusionStatus::INCLUDE; +} + class MockCookieManager : public testing::StrictMock<network::TestCookieManager> { public: @@ -38,7 +48,8 @@ class MockCookieManager // specified value. void ExpectSetCookieCall(const std::string& value) { EXPECT_CALL(*this, SetCanonicalCookie(CookieHasValueMatcher(value), "https", - testing::_, testing::_)); + testing::_, testing::_)) + .With(testing::Args<0, 2>(SetPermittedInContext())); } MOCK_METHOD4( @@ -49,8 +60,7 @@ class MockCookieManager network::mojom::CookieManager::SetCanonicalCookieCallback callback)); }; -class FakeConsistencyCookieManager - : public signin::ConsistencyCookieManagerBase { +class FakeConsistencyCookieManager : public ConsistencyCookieManagerBase { public: FakeConsistencyCookieManager(SigninClient* signin_client, AccountReconcilor* reconcilor) @@ -97,7 +107,7 @@ class ConsistencyCookieManagerTest : public ::testing::Test { MockCookieManager* mock_cookie_manager_ = nullptr; TestSigninClient signin_client_; - identity::IdentityTestEnvironment identity_test_env_; + IdentityTestEnvironment identity_test_env_; std::unique_ptr<AccountReconcilor> reconcilor_; }; diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc index ddb4f9e3da4..bc560b63767 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc @@ -10,8 +10,8 @@ #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_pref_names.h" namespace signin { @@ -32,6 +32,85 @@ bool DiceAccountReconcilorDelegate::IsAccountConsistencyEnforced() const { return account_consistency_ == AccountConsistencyMethod::kDice; } +DiceAccountReconcilorDelegate::InconsistencyReason +DiceAccountReconcilorDelegate::GetInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const { + std::vector<CoreAccountId> valid_gaia_accounts_ids; + for (const gaia::ListedAccount& gaia_account : gaia_accounts) { + if (gaia_account.valid) + valid_gaia_accounts_ids.push_back(gaia_account.id); + } + + bool primary_account_has_token = false; + if (!primary_account.empty()) { + primary_account_has_token = + base::Contains(chrome_accounts, primary_account); + bool primary_account_has_cookie = + base::Contains(valid_gaia_accounts_ids, primary_account); + if (primary_account_has_token && !primary_account_has_cookie) + return InconsistencyReason::kMissingSyncCookie; + + if (!primary_account_has_token && primary_account_has_cookie) + return InconsistencyReason::kSyncAccountAuthError; + } + + bool missing_first_web_account_token = + primary_account.empty() && !gaia_accounts.empty() && + gaia_accounts[0].valid && + !base::Contains(chrome_accounts, gaia_accounts[0].id); + + if (missing_first_web_account_token) + return InconsistencyReason::kMissingFirstWebAccountToken; + + std::sort(valid_gaia_accounts_ids.begin(), valid_gaia_accounts_ids.end()); + std::vector<CoreAccountId> sorted_chrome_accounts(chrome_accounts); + std::sort(sorted_chrome_accounts.begin(), sorted_chrome_accounts.end()); + bool missing_token = + !base::STLIncludes(sorted_chrome_accounts, valid_gaia_accounts_ids); + bool missing_cookie = + !base::STLIncludes(valid_gaia_accounts_ids, sorted_chrome_accounts); + + if (missing_token && missing_cookie) + return InconsistencyReason::kCookieTokenMismatch; + + if (missing_token) + return InconsistencyReason::kMissingSecondaryToken; + + if (missing_cookie) + return InconsistencyReason::kMissingSecondaryCookie; + + if (first_execution && primary_account_has_token && + gaia_accounts[0].id != primary_account && gaia_accounts[0].valid) + return InconsistencyReason::kSyncCookieNotFirst; + + return InconsistencyReason::kNone; +} + +void DiceAccountReconcilorDelegate::MaybeLogInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const { + if (account_consistency_ != AccountConsistencyMethod::kDiceMigration) + return; + + InconsistencyReason inconsistency_reason = GetInconsistencyReason( + primary_account, chrome_accounts, gaia_accounts, first_execution); + + if (first_execution) { + UMA_HISTOGRAM_ENUMERATION( + "Signin.DiceMigrationNotReady.Reason.FirstExecution", + inconsistency_reason); + } else { + UMA_HISTOGRAM_ENUMERATION( + "Signin.DiceMigrationNotReady.Reason.NotFirstExecution", + inconsistency_reason); + } +} + gaia::GaiaSource DiceAccountReconcilorDelegate::GetGaiaApiSource() const { return gaia::GaiaSource::kAccountReconcilorDice; } @@ -46,15 +125,15 @@ gaia::GaiaSource DiceAccountReconcilorDelegate::GetGaiaApiSource() const { // 2. The primary account // 3. The last known first Gaia account // 4. The first account in the token service -std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, +CoreAccountId DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const { bool primary_account_has_token = !primary_account.empty() && - base::ContainsValue(chrome_accounts, primary_account); + base::Contains(chrome_accounts, primary_account); if (gaia_accounts.empty()) { if (primary_account_has_token) @@ -62,22 +141,22 @@ std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( // Try the last known account. This happens when the cookies are cleared // while Sync is disabled. - if (base::ContainsValue(chrome_accounts, last_known_first_account_)) + if (base::Contains(chrome_accounts, last_known_first_account_)) return last_known_first_account_; // As a last resort, use the first Chrome account. - return chrome_accounts.empty() ? std::string() : chrome_accounts[0]; + return chrome_accounts.empty() ? CoreAccountId() : chrome_accounts[0]; } - const std::string& first_gaia_account = gaia_accounts[0].id; + const CoreAccountId& first_gaia_account = gaia_accounts[0].id; bool first_gaia_account_has_token = - base::ContainsValue(chrome_accounts, first_gaia_account); + base::Contains(chrome_accounts, first_gaia_account); if (!first_gaia_account_has_token && (primary_account == first_gaia_account) && gaia_accounts[0].valid) { // The primary account is also the first Gaia account, and has no token. // Logout everything. - return std::string(); + return CoreAccountId(); } // If the primary Chrome account and the default Gaia account are both in @@ -96,7 +175,7 @@ std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( if (first_gaia_account_has_token) return first_gaia_account; // As a last resort, use the first Chrome account. - return chrome_accounts.empty() ? std::string() : chrome_accounts[0]; + return chrome_accounts.empty() ? CoreAccountId() : chrome_accounts[0]; } // While Chrome is running, try the first Gaia account, and then the @@ -108,12 +187,12 @@ std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( // Changing the first Gaia account while Chrome is running would be // confusing for the user. Logout everything. - return std::string(); + return CoreAccountId(); } gaia::MultiloginMode DiceAccountReconcilorDelegate::CalculateModeForReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const { const bool sync_enabled = !primary_account.empty(); @@ -125,10 +204,10 @@ gaia::MultiloginMode DiceAccountReconcilorDelegate::CalculateModeForReconcile( : gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER; } -std::vector<std::string> +std::vector<CoreAccountId> DiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& primary_account, const std::vector<gaia::ListedAccount>& gaia_accounts, const gaia::MultiloginMode mode) const { if (mode == gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER) { @@ -136,7 +215,7 @@ DiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( gaia_accounts); } if (gaia_accounts.empty() && - base::ContainsValue(chrome_accounts, last_known_first_account_)) { + base::Contains(chrome_accounts, last_known_first_account_)) { // In PRESERVE mode in case accounts in cookies are accidentally lost we // should put cached first account first since Gaia has no information about // it. @@ -166,7 +245,7 @@ bool DiceAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() { } void DiceAccountReconcilorDelegate::OnReconcileFinished( - const std::string& first_account, + const CoreAccountId& first_account, bool reconcile_is_noop) { last_known_first_account_ = first_account; diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h index a2dbf416e3c..d62c0375164 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h @@ -8,8 +8,8 @@ #include <string> #include "base/macros.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" +#include "components/signin/public/base/account_consistency_method.h" class SigninClient; @@ -25,29 +25,58 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate { // AccountReconcilorDelegate: bool IsReconcileEnabled() const override; bool IsAccountConsistencyEnforced() const override; + void MaybeLogInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const override; gaia::GaiaSource GetGaiaApiSource() const override; - std::string GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, + CoreAccountId GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const override; RevokeTokenOption ShouldRevokeSecondaryTokensBeforeReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts) override; - void OnReconcileFinished(const std::string& first_account, + void OnReconcileFinished(const CoreAccountId& first_account, bool reconcile_is_noop) override; bool ShouldRevokeTokensOnCookieDeleted() override; private: - std::vector<std::string> GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + // Possible inconsistency reasons between tokens and gaia cookies. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class InconsistencyReason { + // Consistent + kNone = 0, + // Inconsistent + kMissingSyncCookie = 1, + kSyncAccountAuthError = 2, + kMissingFirstWebAccountToken = 3, + kMissingSecondaryCookie = 4, + kMissingSecondaryToken = 5, + kCookieTokenMismatch = 6, + kSyncCookieNotFirst = 7, + kMaxValue = kSyncCookieNotFirst + }; + + // Computes inconsistency reason between tokens and gaia cookies. + InconsistencyReason GetInconsistencyReason( + const CoreAccountId& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const std::vector<gaia::ListedAccount>& gaia_accounts, + bool first_execution) const; + + 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 std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const override; @@ -55,7 +84,7 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate { AccountConsistencyMethod account_consistency_; // Last known "first account". Used when cookies are lost as a best guess. - std::string last_known_first_account_; + CoreAccountId last_known_first_account_; DISALLOW_COPY_AND_ASSIGN(DiceAccountReconcilorDelegate); }; diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc index 4c467559d79..5e3134224b7 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc @@ -7,9 +7,9 @@ #include <vector> #include "components/prefs/pref_registry_simple.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "google_apis/gaia/gaia_auth_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -36,23 +36,21 @@ TEST(DiceAccountReconcilorDelegateTest, RevokeTokens) { // Dice is enabled, revoke only tokens in error state. DiceAccountReconcilorDelegate delegate(&client, AccountConsistencyMethod::kDice); - EXPECT_EQ( - signin::AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError, - delegate.ShouldRevokeSecondaryTokensBeforeReconcile( - std::vector<gaia::ListedAccount>())); + EXPECT_EQ(AccountReconcilorDelegate::RevokeTokenOption::kRevokeIfInError, + delegate.ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>())); } { DiceAccountReconcilorDelegate delegate( &client, AccountConsistencyMethod::kDiceMigration); // Gaia accounts are not empty, don't revoke. gaia::ListedAccount gaia_account; - gaia_account.id = "other"; - EXPECT_EQ( - signin::AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke, - delegate.ShouldRevokeSecondaryTokensBeforeReconcile( - std::vector<gaia::ListedAccount>{gaia_account})); + gaia_account.id = CoreAccountId("other"); + EXPECT_EQ(AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke, + delegate.ShouldRevokeSecondaryTokensBeforeReconcile( + std::vector<gaia::ListedAccount>{gaia_account})); // Revoke. - EXPECT_EQ(signin::AccountReconcilorDelegate::RevokeTokenOption::kRevoke, + EXPECT_EQ(AccountReconcilorDelegate::RevokeTokenOption::kRevoke, delegate.ShouldRevokeSecondaryTokensBeforeReconcile( std::vector<gaia::ListedAccount>())); } @@ -71,7 +69,8 @@ TEST(DiceAccountReconcilorDelegateTest, OnReconcileFinished) { EXPECT_CALL(client, SetReadyForDiceMigration(false)).Times(1); DiceAccountReconcilorDelegate delegate( &client, AccountConsistencyMethod::kDiceMigration); - delegate.OnReconcileFinished("account", true /* is_reconcile_noop */); + delegate.OnReconcileFinished(CoreAccountId("account"), + true /* is_reconcile_noop */); } pref_service.SetBoolean(prefs::kTokenServiceDiceCompatible, true); @@ -83,7 +82,8 @@ TEST(DiceAccountReconcilorDelegateTest, OnReconcileFinished) { EXPECT_CALL(client, SetReadyForDiceMigration(false)).Times(1); DiceAccountReconcilorDelegate delegate( &client, AccountConsistencyMethod::kDiceMigration); - delegate.OnReconcileFinished("account", false /* is_reconcile_noop */); + delegate.OnReconcileFinished(CoreAccountId("account"), + false /* is_reconcile_noop */); } { @@ -92,7 +92,8 @@ TEST(DiceAccountReconcilorDelegateTest, OnReconcileFinished) { EXPECT_CALL(client, SetReadyForDiceMigration(true)).Times(1); DiceAccountReconcilorDelegate delegate( &client, AccountConsistencyMethod::kDiceMigration); - delegate.OnReconcileFinished("account", true /* is_reconcile_noop */); + delegate.OnReconcileFinished(CoreAccountId("account"), + true /* is_reconcile_noop */); } } diff --git a/chromium/components/signin/core/browser/dice_header_helper.cc b/chromium/components/signin/core/browser/dice_header_helper.cc index eac6a7c18b4..0c9880ed4c7 100644 --- a/chromium/components/signin/core/browser/dice_header_helper.cc +++ b/chromium/components/signin/core/browser/dice_header_helper.cc @@ -50,7 +50,7 @@ DiceAction GetDiceActionFromHeader(const std::string& value) { } // namespace DiceHeaderHelper::DiceHeaderHelper(AccountConsistencyMethod account_consistency) - : SigninHeaderHelper("Dice"), account_consistency_(account_consistency) {} + : account_consistency_(account_consistency) {} // static DiceResponseParams DiceHeaderHelper::BuildDiceSigninResponseParams( diff --git a/chromium/components/signin/core/browser/dice_header_helper.h b/chromium/components/signin/core/browser/dice_header_helper.h index cd725f274e1..d4711431582 100644 --- a/chromium/components/signin/core/browser/dice_header_helper.h +++ b/chromium/components/signin/core/browser/dice_header_helper.h @@ -8,8 +8,8 @@ #include <string> #include "base/macros.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/signin_header_helper.h" +#include "components/signin/public/base/account_consistency_method.h" class GURL; diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc deleted file mode 100644 index 4d2899e7550..00000000000 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2013 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/fake_profile_oauth2_token_service.h" - -#include <memory> - -#include "base/bind.h" -#include "base/location.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "google_apis/gaia/fake_oauth2_token_service_delegate.h" -#include "services/network/public/cpp/shared_url_loader_factory.h" - -FakeProfileOAuth2TokenService::PendingRequest::PendingRequest() {} - -FakeProfileOAuth2TokenService::PendingRequest::PendingRequest( - const PendingRequest& other) = default; - -FakeProfileOAuth2TokenService::PendingRequest::~PendingRequest() {} - -FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService( - PrefService* user_prefs) - : FakeProfileOAuth2TokenService( - user_prefs, - std::make_unique<FakeOAuth2TokenServiceDelegate>()) {} - -FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService( - PrefService* user_prefs, - std::unique_ptr<OAuth2TokenServiceDelegate> delegate) - : ProfileOAuth2TokenService(user_prefs, std::move(delegate)), - auto_post_fetch_response_on_message_loop_(false), - weak_ptr_factory_(this) {} - -FakeProfileOAuth2TokenService::~FakeProfileOAuth2TokenService() {} - -void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( - const std::string& account_id, - const std::string& access_token, - const base::Time& expiration) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests(account_id, true, ScopeSet(), - GoogleServiceAuthError::AuthErrorNone(), - OAuth2AccessTokenConsumer::TokenResponse( - access_token, expiration, std::string() /* id_token */)); -} - -void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( - const std::string& account_id, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests(account_id, true, ScopeSet(), - GoogleServiceAuthError::AuthErrorNone(), token_response); -} - -void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequestsForAccount( - const std::string& account_id, - const GoogleServiceAuthError& error) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests(account_id, true, ScopeSet(), error, - OAuth2AccessTokenConsumer::TokenResponse()); -} - -void FakeProfileOAuth2TokenService::IssueTokenForScope( - const ScopeSet& scope, - const std::string& access_token, - const base::Time& expiration) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(), - OAuth2AccessTokenConsumer::TokenResponse( - access_token, expiration, std::string() /* id_token */)); -} - -void FakeProfileOAuth2TokenService::IssueTokenForScope( - const ScopeSet& scope, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", false, scope, GoogleServiceAuthError::AuthErrorNone(), - token_response); -} - -void FakeProfileOAuth2TokenService::IssueErrorForScope( - const ScopeSet& scope, - const GoogleServiceAuthError& error) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", false, scope, error, - OAuth2AccessTokenConsumer::TokenResponse()); -} - -void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequests( - const GoogleServiceAuthError& error) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", true, ScopeSet(), error, - OAuth2AccessTokenConsumer::TokenResponse()); -} - -void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests( - const std::string& access_token, - const base::Time& expiration) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", true, ScopeSet(), - GoogleServiceAuthError::AuthErrorNone(), - OAuth2AccessTokenConsumer::TokenResponse( - access_token, expiration, std::string() /* id_token */)); -} - -void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests( - const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - DCHECK(!auto_post_fetch_response_on_message_loop_); - CompleteRequests("", true, ScopeSet(), - GoogleServiceAuthError::AuthErrorNone(), token_response); -} - -void FakeProfileOAuth2TokenService::CompleteRequests( - const std::string& account_id, - bool all_scopes, - const ScopeSet& scope, - const GoogleServiceAuthError& error, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests = - GetPendingRequests(); - - // Walk the requests and notify the callbacks. - for (auto it = requests.begin(); it != requests.end(); ++it) { - DCHECK(it->request); - - bool scope_matches = all_scopes || it->scopes == scope; - bool account_matches = account_id.empty() || account_id == it->account_id; - if (account_matches && scope_matches) { - for (auto& diagnostic_observer : GetDiagnicsObservers()) { - diagnostic_observer.OnFetchAccessTokenComplete( - account_id, it->request->GetConsumerId(), scope, error, - base::Time()); - } - - it->request->InformConsumer( - error, OAuth2AccessTokenConsumer::TokenResponse( - token_response.access_token, - token_response.expiration_time, token_response.id_token)); - } - } -} - -std::vector<FakeProfileOAuth2TokenService::PendingRequest> -FakeProfileOAuth2TokenService::GetPendingRequests() { - std::vector<PendingRequest> valid_requests; - for (auto it = pending_requests_.begin(); it != pending_requests_.end(); - ++it) { - if (it->request) - valid_requests.push_back(*it); - } - return valid_requests; -} - -void FakeProfileOAuth2TokenService::CancelAllRequests() { - CompleteRequests( - "", true, ScopeSet(), - GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), - OAuth2AccessTokenConsumer::TokenResponse()); -} - -void FakeProfileOAuth2TokenService::CancelRequestsForAccount( - const std::string& account_id) { - CompleteRequests( - account_id, true, ScopeSet(), - GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), - OAuth2AccessTokenConsumer::TokenResponse()); -} - -void FakeProfileOAuth2TokenService::FetchOAuth2Token( - RequestImpl* request, - const std::string& account_id, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const std::string& client_id, - const std::string& client_secret, - const ScopeSet& scopes) { - PendingRequest pending_request; - pending_request.account_id = account_id; - pending_request.client_id = client_id; - pending_request.client_secret = client_secret; - pending_request.url_loader_factory = url_loader_factory; - pending_request.scopes = scopes; - pending_request.request = request->AsWeakPtr(); - pending_requests_.push_back(pending_request); - - if (auto_post_fetch_response_on_message_loop_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&FakeProfileOAuth2TokenService::CompleteRequests, - weak_ptr_factory_.GetWeakPtr(), account_id, - /*all_scoped=*/true, scopes, - GoogleServiceAuthError::AuthErrorNone(), - OAuth2AccessTokenConsumer::TokenResponse( - "access_token", base::Time::Max(), std::string()))); - } -} - -void FakeProfileOAuth2TokenService::InvalidateAccessTokenImpl( - const std::string& account_id, - const std::string& client_id, - const ScopeSet& scopes, - const std::string& access_token) { - // Do nothing, as we don't have a cache from which to remove the token. -} diff --git a/chromium/components/signin/core/browser/identity_manager_wrapper.cc b/chromium/components/signin/core/browser/identity_manager_wrapper.cc deleted file mode 100644 index 2025ebcd649..00000000000 --- a/chromium/components/signin/core/browser/identity_manager_wrapper.cc +++ /dev/null @@ -1,31 +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/identity_manager_wrapper.h" - -#include "components/keyed_service/core/keyed_service.h" -#include "services/identity/public/cpp/accounts_cookie_mutator.h" -#include "services/identity/public/cpp/accounts_mutator.h" -#include "services/identity/public/cpp/diagnostics_provider.h" -#include "services/identity/public/cpp/primary_account_mutator.h" - -IdentityManagerWrapper::IdentityManagerWrapper( - std::unique_ptr<AccountTrackerService> account_tracker_service, - std::unique_ptr<ProfileOAuth2TokenService> token_service, - std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, - std::unique_ptr<SigninManagerBase> signin_manager, - std::unique_ptr<AccountFetcherService> account_fetcher_service, - std::unique_ptr<identity::PrimaryAccountMutator> primary_account_mutator, - std::unique_ptr<identity::AccountsMutator> accounts_mutator, - std::unique_ptr<identity::AccountsCookieMutator> accounts_cookie_mutator, - std::unique_ptr<identity::DiagnosticsProvider> diagnostics_provider) - : identity::IdentityManager(std::move(account_tracker_service), - std::move(token_service), - std::move(gaia_cookie_manager_service), - std::move(signin_manager), - std::move(account_fetcher_service), - std::move(primary_account_mutator), - std::move(accounts_mutator), - std::move(accounts_cookie_mutator), - std::move(diagnostics_provider)) {} diff --git a/chromium/components/signin/core/browser/identity_manager_wrapper.h b/chromium/components/signin/core/browser/identity_manager_wrapper.h deleted file mode 100644 index b1c03249a61..00000000000 --- a/chromium/components/signin/core/browser/identity_manager_wrapper.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_IDENTITY_MANAGER_WRAPPER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_IDENTITY_MANAGER_WRAPPER_H_ - -#include "components/keyed_service/core/keyed_service.h" -#include "services/identity/public/cpp/identity_manager.h" - -class ProfileOAuth2TokenService; -class SigninManagerBase; -class GaiaCookieManagerService; -class AccountFetcherService; -class SigninManagerBase; -class AccountsMutator; - -namespace identity { -class PrimaryAccountMutator; -class AccountsCookieMutator; -class DiagnosticsProvider; -} // namespace identity - -// Subclass that wraps IdentityManager in a KeyedService. -// TODO(https://crbug.com/952788): This class can be deleted if -// IdentityManager is updated to inherit from KeyedService directly. -class IdentityManagerWrapper : public KeyedService, - public identity::IdentityManager { - public: - IdentityManagerWrapper( - std::unique_ptr<AccountTrackerService> account_tracker_service, - std::unique_ptr<ProfileOAuth2TokenService> token_service, - std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, - std::unique_ptr<SigninManagerBase> signin_manager, - std::unique_ptr<AccountFetcherService> account_fetcher_service, - std::unique_ptr<identity::PrimaryAccountMutator> primary_account_mutator, - std::unique_ptr<identity::AccountsMutator> accounts_mutator, - std::unique_ptr<identity::AccountsCookieMutator> accounts_cookie_mutator, - std::unique_ptr<identity::DiagnosticsProvider> diagnostics_provider); -}; - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_IDENTITY_MANAGER_WRAPPER_H_ diff --git a/chromium/components/signin/core/browser/identity_utils.h b/chromium/components/signin/core/browser/identity_utils.h deleted file mode 100644 index 241066b6c22..00000000000 --- a/chromium/components/signin/core/browser/identity_utils.h +++ /dev/null @@ -1,40 +0,0 @@ -// 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_CORE_BROWSER_IDENTITY_UTILS_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_IDENTITY_UTILS_H_ - -#include "base/strings/string_piece.h" - -class PrefService; - -namespace identity { - -// Returns true if the username is allowed based on the pattern string. -// -// NOTE: Can be moved to //services/identity/public/cpp once SigninManager is -// moved to //services/identity. -bool IsUsernameAllowedByPattern(base::StringPiece username, - base::StringPiece pattern); - -// Returns true if the username is either allowed based on a pattern registered -// as |pattern_pref_name| with the preferences service referenced by |prefs|, -// or if such pattern can't be retrieved from |prefs|. This is a legacy -// method intended to be used to migrate from SigninManager::IsAllowedUsername() -// only while SigninManager::Initialize() can still accept a null PrefService*, -// and can be removed once that's no longer the case (see crbug.com/908121). -bool LegacyIsUsernameAllowedByPatternFromPrefs( - PrefService* prefs, - const std::string& username, - const std::string& pattern_pref_name); -} // namespace identity - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_IDENTITY_UTILS_H_ diff --git a/chromium/components/signin/core/browser/identity_utils_unittest.cc b/chromium/components/signin/core/browser/identity_utils_unittest.cc deleted file mode 100644 index bdb7f76bc84..00000000000 --- a/chromium/components/signin/core/browser/identity_utils_unittest.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/identity_utils.h" - -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/pref_service.h" -#include "components/prefs/testing_pref_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { -const char kUsername[] = "test@test.com"; - -const char kValidWildcardPattern[] = ".*@test.com"; -const char kInvalidWildcardPattern[] = "*@test.com"; - -const char kMatchingPattern1[] = "test@test.com"; -const char kMatchingPattern2[] = ".*@test.com"; -const char kMatchingPattern3[] = "test@.*.com"; -const char kMatchingPattern4[] = ".*@.*.com"; -const char kMatchingPattern5[] = ".*@.*"; -const char kMatchingPattern6[] = ".*"; - -const char kNonMatchingPattern[] = ".*foo.*"; -const char kNonMatchingUsernamePattern[] = "foo@test.com"; -const char kNonMatchingDomainPattern[] = "test@foo.com"; - -const char kRegisteredPattern[] = "test.registered.username_pattern"; -} // namespace - -class IdentityUtilsTest : public testing::Test { - public: - IdentityUtilsTest() { - prefs_.registry()->RegisterStringPref(kRegisteredPattern, std::string()); - } - - TestingPrefServiceSimple* prefs() { return &prefs_; } - - private: - TestingPrefServiceSimple prefs_; -}; - -TEST_F(IdentityUtilsTest, IsUsernameAllowedByPattern_EmptyPatterns) { - EXPECT_TRUE(identity::IsUsernameAllowedByPattern(kUsername, "")); - EXPECT_FALSE(identity::IsUsernameAllowedByPattern(kUsername, " ")); -} - -TEST_F(IdentityUtilsTest, IsUsernameAllowedByPattern_InvalidWildcardPatterns) { - // identity::IsUsernameAllowedByPattern should recognize invalid wildcard - // patterns like "*@foo.com" and insert a "." before them automatically. - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kValidWildcardPattern)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kInvalidWildcardPattern)); -} - -TEST_F(IdentityUtilsTest, IsUsernameAllowedByPattern_MatchingWildcardPatterns) { - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern1)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern2)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern3)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern4)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern5)); - EXPECT_TRUE( - identity::IsUsernameAllowedByPattern(kUsername, kMatchingPattern6)); - - EXPECT_FALSE( - identity::IsUsernameAllowedByPattern(kUsername, kNonMatchingPattern)); - EXPECT_FALSE(identity::IsUsernameAllowedByPattern( - kUsername, kNonMatchingUsernamePattern)); - EXPECT_FALSE(identity::IsUsernameAllowedByPattern(kUsername, - kNonMatchingDomainPattern)); -} - -TEST_F(IdentityUtilsTest, LegacyIsUsernameAllowedByPatternFromPrefs) { - // Passing a null local state should return 'allowed'. - EXPECT_TRUE(identity::LegacyIsUsernameAllowedByPatternFromPrefs( - nullptr, kUsername, kRegisteredPattern)); - - // Now set different values for the named pattern and check against them. - prefs()->SetString(kRegisteredPattern, kMatchingPattern1); - EXPECT_TRUE(identity::LegacyIsUsernameAllowedByPatternFromPrefs( - prefs(), kUsername, kRegisteredPattern)); - - prefs()->SetString(kRegisteredPattern, kNonMatchingUsernamePattern); - EXPECT_FALSE(identity::LegacyIsUsernameAllowedByPatternFromPrefs( - prefs(), kUsername, kRegisteredPattern)); - - prefs()->SetString(kRegisteredPattern, kMatchingPattern2); - EXPECT_TRUE(identity::LegacyIsUsernameAllowedByPatternFromPrefs( - prefs(), kUsername, kRegisteredPattern)); - - prefs()->SetString(kRegisteredPattern, kNonMatchingDomainPattern); - EXPECT_FALSE(identity::LegacyIsUsernameAllowedByPatternFromPrefs( - prefs(), kUsername, kRegisteredPattern)); -} diff --git a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc index 86bcfa4143a..8b8c9369672 100644 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.cc @@ -5,7 +5,6 @@ #include "components/signin/core/browser/mice_account_reconcilor_delegate.h" #include "base/logging.h" -#include "components/signin/core/browser/account_reconcilor.h" namespace signin { @@ -25,22 +24,22 @@ gaia::GaiaSource MiceAccountReconcilorDelegate::GetGaiaApiSource() const { return gaia::GaiaSource::kAccountReconcilorMirror; } -std::string MiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, +CoreAccountId MiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + 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 std::string(); + return CoreAccountId(); } -std::vector<std::string> +std::vector<CoreAccountId> MiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + 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()) @@ -51,10 +50,9 @@ MiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( // - 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. - std::string new_first_account = - base::ContainsValue(chrome_accounts, primary_account) - ? primary_account - : chrome_accounts[0]; + 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. @@ -64,7 +62,7 @@ MiceAccountReconcilorDelegate::GetChromeAccountsForReconcile( gaia::MultiloginMode MiceAccountReconcilorDelegate::CalculateModeForReconcile( const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const { return gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER; diff --git a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h index b5d080b0129..28a71d00bc1 100644 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate.h @@ -24,20 +24,20 @@ class MiceAccountReconcilorDelegate : public AccountReconcilorDelegate { bool IsReconcileEnabled() const override; bool IsAccountConsistencyEnforced() const override; gaia::GaiaSource GetGaiaApiSource() const override; - std::string GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, + CoreAccountId GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const override; - std::vector<std::string> GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + 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 std::string primary_account, + const CoreAccountId& primary_account, bool first_execution, bool primary_has_error) const override; bool IsUnknownInvalidAccountInCookieAllowed() const override; 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 index 001637f6376..b3947eaab72 100644 --- a/chromium/components/signin/core/browser/mice_account_reconcilor_delegate_unittest.cc +++ b/chromium/components/signin/core/browser/mice_account_reconcilor_delegate_unittest.cc @@ -17,11 +17,20 @@ namespace { gaia::ListedAccount BuildTestListedAccount(const std::string account_id, bool valid) { gaia::ListedAccount account; - account.id = account_id; + 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) { @@ -65,11 +74,13 @@ TEST(MiceAccountReconcilorDelegate, CalculateParametersForMultilogin) { for (const auto& test : cases) { MultiloginParameters multilogin_parameters = mice_delegate.CalculateParametersForMultilogin( - test.chrome_accounts, test.primary_account, test.gaia_accounts, - false, false); + 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(test.expected_accounts, multilogin_parameters.accounts_to_send); + EXPECT_EQ(ToAccountIdList(test.expected_accounts), + multilogin_parameters.accounts_to_send); } } diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc index dec959175c4..db56be5a380 100644 --- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc @@ -10,7 +10,7 @@ namespace signin { MirrorAccountReconcilorDelegate::MirrorAccountReconcilorDelegate( - identity::IdentityManager* identity_manager) + IdentityManager* identity_manager) : identity_manager_(identity_manager) { DCHECK(identity_manager_); identity_manager_->AddObserver(this); @@ -37,22 +37,22 @@ bool MirrorAccountReconcilorDelegate::ShouldAbortReconcileIfPrimaryHasError() return true; } -std::string MirrorAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, +CoreAccountId MirrorAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const { // Mirror only uses the primary account, and it is never empty. DCHECK(!primary_account.empty()); - DCHECK(base::ContainsValue(chrome_accounts, primary_account)); + DCHECK(base::Contains(chrome_accounts, primary_account)); return primary_account; } -std::vector<std::string> +std::vector<CoreAccountId> MirrorAccountReconcilorDelegate::GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + const std::vector<CoreAccountId>& chrome_accounts, + const CoreAccountId& primary_account, const std::vector<gaia::ListedAccount>& gaia_accounts, const gaia::MultiloginMode mode) const { DCHECK_EQ(mode, 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 a13904754a1..bc465861119 100644 --- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h @@ -10,17 +10,15 @@ #include "base/macros.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "services/identity/public/cpp/identity_manager.h" +#include "components/signin/public/identity_manager/identity_manager.h" namespace signin { // AccountReconcilorDelegate specialized for Mirror. -class MirrorAccountReconcilorDelegate - : public AccountReconcilorDelegate, - public identity::IdentityManager::Observer { +class MirrorAccountReconcilorDelegate : public AccountReconcilorDelegate, + public IdentityManager::Observer { public: - explicit MirrorAccountReconcilorDelegate( - identity::IdentityManager* identity_manager); + explicit MirrorAccountReconcilorDelegate(IdentityManager* identity_manager); ~MirrorAccountReconcilorDelegate() override; protected: @@ -34,15 +32,15 @@ class MirrorAccountReconcilorDelegate bool IsAccountConsistencyEnforced() const override; gaia::GaiaSource GetGaiaApiSource() const override; bool ShouldAbortReconcileIfPrimaryHasError() const override; - std::string GetFirstGaiaAccountForReconcile( - const std::vector<std::string>& chrome_accounts, + CoreAccountId GetFirstGaiaAccountForReconcile( + const std::vector<CoreAccountId>& chrome_accounts, const std::vector<gaia::ListedAccount>& gaia_accounts, - const std::string& primary_account, + const CoreAccountId& primary_account, bool first_execution, bool will_logout) const override; - std::vector<std::string> GetChromeAccountsForReconcile( - const std::vector<std::string>& chrome_accounts, - const std::string& primary_account, + 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; @@ -52,7 +50,7 @@ class MirrorAccountReconcilorDelegate void OnPrimaryAccountCleared( const CoreAccountInfo& previous_primary_account_info) override; - identity::IdentityManager* identity_manager_; + IdentityManager* identity_manager_; DISALLOW_COPY_AND_ASSIGN(MirrorAccountReconcilorDelegate); }; diff --git a/chromium/components/signin/core/browser/multilogin_parameters.cc b/chromium/components/signin/core/browser/multilogin_parameters.cc new file mode 100644 index 00000000000..6afad08509a --- /dev/null +++ b/chromium/components/signin/core/browser/multilogin_parameters.cc @@ -0,0 +1,28 @@ +// 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/multilogin_parameters.h" + +namespace signin { + +MultiloginParameters::MultiloginParameters( + const gaia::MultiloginMode mode, + const std::vector<CoreAccountId>& accounts_to_send) + : mode(mode), accounts_to_send(accounts_to_send) {} + +MultiloginParameters::~MultiloginParameters() {} + +MultiloginParameters::MultiloginParameters(const MultiloginParameters& other) { + mode = other.mode; + accounts_to_send = other.accounts_to_send; +} + +MultiloginParameters& MultiloginParameters::operator=( + const MultiloginParameters& other) { + mode = other.mode; + accounts_to_send = other.accounts_to_send; + return *this; +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/multilogin_parameters.h b/chromium/components/signin/core/browser/multilogin_parameters.h new file mode 100644 index 00000000000..9dae3b26b49 --- /dev/null +++ b/chromium/components/signin/core/browser/multilogin_parameters.h @@ -0,0 +1,33 @@ +// 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_MULTILOGIN_PARAMETERS_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_MULTILOGIN_PARAMETERS_H_ + +#include <string> +#include <vector> + +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" + +namespace signin { + +struct MultiloginParameters { + MultiloginParameters(gaia::MultiloginMode mode, + const std::vector<CoreAccountId>& accounts_to_send); + MultiloginParameters(const MultiloginParameters& other); + MultiloginParameters& operator=(const MultiloginParameters& other); + ~MultiloginParameters(); + + // Needed for testing. + bool operator==(const MultiloginParameters& other) const { + return mode == other.mode && accounts_to_send == other.accounts_to_send; + } + + gaia::MultiloginMode mode; + std::vector<CoreAccountId> accounts_to_send; +}; +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_MULTILOGIN_PARAMETERS_H_ diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h deleted file mode 100644 index 168ba881513..00000000000 --- a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h +++ /dev/null @@ -1,74 +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_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ - -#include <map> -#include <memory> -#include <set> -#include <string> -#include <vector> - -#include "base/bind_helpers.h" -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "google_apis/gaia/gaia_auth_fetcher.h" -#include "google_apis/gaia/google_service_auth_error.h" -#include "google_apis/gaia/oauth2_token_service.h" - -class SigninClient; - -namespace signin { - -// Fetches multilogin access tokens in parallel for multiple accounts. -// It is safe to delete this object from within the callbacks. -class OAuthMultiloginTokenFetcher : public OAuth2TokenService::Consumer { - public: - using SuccessCallback = base::OnceCallback<void( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&)>; - using FailureCallback = - base::OnceCallback<void(const GoogleServiceAuthError&)>; - - OAuthMultiloginTokenFetcher(SigninClient* signin_client, - OAuth2TokenService* token_service, - const std::vector<std::string>& account_ids, - SuccessCallback success_callback, - FailureCallback failure_callback); - - ~OAuthMultiloginTokenFetcher() override; - - private: - void StartFetchingToken(const std::string& account_id); - - // Overridden from OAuth2TokenService::Consumer. - void OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) override; - - // Helper function to remove a request from token_requests_. - void EraseRequest(const OAuth2TokenService::Request* request); - - SigninClient* signin_client_; - OAuth2TokenService* token_service_; - const std::vector<std::string> account_ids_; - - SuccessCallback success_callback_; - FailureCallback failure_callback_; - - std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_; - std::map<std::string, std::string> access_tokens_; - std::set<std::string> retried_requests_; // Requests are retried once. - - base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginTokenFetcher); -}; - -} // namespace signin - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc deleted file mode 100644 index 50fb83dd5ce..00000000000 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc +++ /dev/null @@ -1,233 +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. - -#include "components/signin/core/browser/profile_oauth2_token_service.h" - -#include "base/auto_reset.h" -#include "base/logging.h" -#include "build/build_config.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/signin/core/browser/device_id_helper.h" -#include "components/signin/core/browser/signin_pref_names.h" - -using signin_metrics::SourceForRefreshTokenOperation; - -namespace { -std::string SourceToString(SourceForRefreshTokenOperation source) { - switch (source) { - case SourceForRefreshTokenOperation::kUnknown: - return "Unknown"; - case SourceForRefreshTokenOperation::kTokenService_LoadCredentials: - return "TokenService::LoadCredentials"; - case SourceForRefreshTokenOperation::kSupervisedUser_InitSync: - return "SupervisedUser::InitSync"; - case SourceForRefreshTokenOperation::kInlineLoginHandler_Signin: - return "InlineLoginHandler::Signin"; - case SourceForRefreshTokenOperation::kSigninManager_ClearPrimaryAccount: - return "SigninManager::ClearPrimaryAccount"; - case SourceForRefreshTokenOperation::kSigninManager_LegacyPreDiceSigninFlow: - return "SigninManager::LegacyPreDiceSigninFlow"; - case SourceForRefreshTokenOperation::kUserMenu_RemoveAccount: - return "UserMenu::RemoveAccount"; - case SourceForRefreshTokenOperation::kUserMenu_SignOutAllAccounts: - return "UserMenu::SignOutAllAccounts"; - case SourceForRefreshTokenOperation::kSettings_Signout: - return "Settings::Signout"; - case SourceForRefreshTokenOperation::kSettings_PauseSync: - return "Settings::PauseSync"; - case SourceForRefreshTokenOperation:: - kAccountReconcilor_GaiaCookiesDeletedByUser: - return "AccountReconcilor::GaiaCookiesDeletedByUser"; - case SourceForRefreshTokenOperation::kAccountReconcilor_GaiaCookiesUpdated: - return "AccountReconcilor::GaiaCookiesUpdated"; - case SourceForRefreshTokenOperation::kAccountReconcilor_Reconcile: - return "AccountReconcilor::Reconcile"; - case SourceForRefreshTokenOperation::kDiceResponseHandler_Signin: - return "DiceResponseHandler::Signin"; - case SourceForRefreshTokenOperation::kDiceResponseHandler_Signout: - return "DiceResponseHandler::Signout"; - case SourceForRefreshTokenOperation::kDiceTurnOnSyncHelper_Abort: - return "DiceTurnOnSyncHelper::Abort"; - case SourceForRefreshTokenOperation::kMachineLogon_CredentialProvider: - return "MachineLogon::CredentialProvider"; - case SourceForRefreshTokenOperation::kTokenService_ExtractCredentials: - return "TokenService::ExtractCredentials"; - } -} -} // namespace - -ProfileOAuth2TokenService::ProfileOAuth2TokenService( - PrefService* user_prefs, - std::unique_ptr<OAuth2TokenServiceDelegate> delegate) - : OAuth2TokenService(std::move(delegate)), - user_prefs_(user_prefs), - all_credentials_loaded_(false) { - DCHECK(user_prefs_); - AddObserver(this); -} - -ProfileOAuth2TokenService::~ProfileOAuth2TokenService() { - RemoveObserver(this); -} - -// static -void ProfileOAuth2TokenService::RegisterProfilePrefs( - PrefRegistrySimple* registry) { -#if defined(OS_IOS) - registry->RegisterBooleanPref(prefs::kTokenServiceExcludeAllSecondaryAccounts, - false); - registry->RegisterListPref(prefs::kTokenServiceExcludedSecondaryAccounts); -#endif - registry->RegisterStringPref(prefs::kGoogleServicesSigninScopedDeviceId, - std::string()); -} - -void ProfileOAuth2TokenService::Shutdown() { - CancelAllRequests(); - GetDelegate()->Shutdown(); -} - -void ProfileOAuth2TokenService::LoadCredentials( - const std::string& primary_account_id) { - DCHECK_EQ(SourceForRefreshTokenOperation::kUnknown, - update_refresh_token_source_); - update_refresh_token_source_ = - SourceForRefreshTokenOperation::kTokenService_LoadCredentials; - GetDelegate()->LoadCredentials(primary_account_id); -} - -bool ProfileOAuth2TokenService::AreAllCredentialsLoaded() { - return all_credentials_loaded_; -} - -void ProfileOAuth2TokenService::UpdateCredentials( - const std::string& account_id, - const std::string& refresh_token, - SourceForRefreshTokenOperation source) { - base::AutoReset<SourceForRefreshTokenOperation> auto_reset( - &update_refresh_token_source_, source); - GetDelegate()->UpdateCredentials(account_id, refresh_token); -} - -void ProfileOAuth2TokenService::RevokeCredentials( - const std::string& account_id, - SourceForRefreshTokenOperation source) { - base::AutoReset<SourceForRefreshTokenOperation> auto_reset( - &update_refresh_token_source_, source); - GetDelegate()->RevokeCredentials(account_id); -} - -void ProfileOAuth2TokenService::RevokeAllCredentials( - SourceForRefreshTokenOperation source) { - base::AutoReset<SourceForRefreshTokenOperation> auto_reset( - &update_refresh_token_source_, source); - CancelAllRequests(); - ClearCache(); - GetDelegate()->RevokeAllCredentials(); -} - -const net::BackoffEntry* ProfileOAuth2TokenService::GetDelegateBackoffEntry() { - return GetDelegate()->BackoffEntry(); -} - -#if BUILDFLAG(ENABLE_DICE_SUPPORT) -void ProfileOAuth2TokenService::ExtractCredentials( - ProfileOAuth2TokenService* to_service, - const std::string& account_id) { - base::AutoReset<SourceForRefreshTokenOperation> auto_reset( - &update_refresh_token_source_, - SourceForRefreshTokenOperation::kTokenService_ExtractCredentials); - GetDelegate()->ExtractCredentials(to_service, account_id); -} -#endif - -void ProfileOAuth2TokenService::OnRefreshTokenAvailable( - const std::string& account_id) { - // Check if the newly-updated token is valid (invalid tokens are inserted when - // the user signs out on the web with DICE enabled). - bool is_valid = true; - GoogleServiceAuthError token_error = GetAuthError(account_id); - if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( - GoogleServiceAuthError::InvalidGaiaCredentialsReason:: - CREDENTIALS_REJECTED_BY_CLIENT)) { - is_valid = false; - } - - CancelRequestsForAccount(account_id); - ClearCacheForAccount(account_id); - - signin_metrics::RecordRefreshTokenUpdatedFromSource( - is_valid, update_refresh_token_source_); - - std::string source_string = SourceToString(update_refresh_token_source_); - for (auto& diagnostic_observer : GetDiagnicsObservers()) { - diagnostic_observer.OnRefreshTokenAvailableFromSource(account_id, is_valid, - source_string); - } -} - -void ProfileOAuth2TokenService::OnRefreshTokenRevoked( - const std::string& account_id) { - // If this was the last token, recreate the device ID. - RecreateDeviceIdIfNeeded(); - - CancelRequestsForAccount(account_id); - ClearCacheForAccount(account_id); - - signin_metrics::RecordRefreshTokenRevokedFromSource( - update_refresh_token_source_); - std::string source_string = SourceToString(update_refresh_token_source_); - for (auto& diagnostic_observer : GetDiagnicsObservers()) { - diagnostic_observer.OnRefreshTokenRevokedFromSource(account_id, - source_string); - } -} - -void ProfileOAuth2TokenService::OnRefreshTokensLoaded() { - DCHECK_NE(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, - GetDelegate()->load_credentials_state()); - DCHECK_NE(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, - GetDelegate()->load_credentials_state()); - - all_credentials_loaded_ = true; - - // Reset the state for update refresh token operations to Unknown as this - // was the original state before LoadCredentials was called. - update_refresh_token_source_ = SourceForRefreshTokenOperation::kUnknown; - - // Ensure the device ID is not empty, and recreate it if all tokens were - // cleared during the loading process. - RecreateDeviceIdIfNeeded(); -} - -bool ProfileOAuth2TokenService::HasLoadCredentialsFinishedWithNoErrors() { - switch (GetDelegate()->load_credentials_state()) { - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED: - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS: - // LoadCredentials has not finished. - return false; - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: - case OAuth2TokenServiceDelegate:: - LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS: - case OAuth2TokenServiceDelegate:: - LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS: - // LoadCredentials finished, but with errors - return false; - case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: - case OAuth2TokenServiceDelegate:: - LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT: - // Load credentials finished with success. - return true; - } -} - -void ProfileOAuth2TokenService::RecreateDeviceIdIfNeeded() { -// On ChromeOS the device ID is not managed by the token service. -#if !defined(OS_CHROMEOS) - if (AreAllCredentialsLoaded() && HasLoadCredentialsFinishedWithNoErrors() && - GetAccounts().empty()) { - signin::RecreateSigninScopedDeviceId(user_prefs_); - } -#endif -} diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.h b/chromium/components/signin/core/browser/profile_oauth2_token_service.h deleted file mode 100644 index c985a241111..00000000000 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service.h +++ /dev/null @@ -1,133 +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. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ - -#include <string> - -#include "base/macros.h" -#include "build/buildflag.h" -#include "components/keyed_service/core/keyed_service.h" -#include "components/signin/core/browser/signin_buildflags.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" -#include "net/base/backoff_entry.h" - -#include <memory> - -namespace identity { -class IdentityManager; -} - -class PrefService; -class PrefRegistrySimple; - -// ProfileOAuth2TokenService is a KeyedService that retrieves -// OAuth2 access tokens for a given set of scopes using the OAuth2 login -// refresh tokens. -// -// See |OAuth2TokenService| for usage details. -// -// Note: after StartRequest returns, in-flight requests will continue -// even if the TokenService refresh token that was used to initiate -// the request changes or is cleared. When the request completes, -// Consumer::OnGetTokenSuccess will be invoked, but the access token -// won't be cached. -// -// Note: requests should be started from the UI thread. To start a -// request from other thread, please use OAuth2TokenServiceRequest. -class ProfileOAuth2TokenService : public OAuth2TokenService, - public OAuth2TokenService::Observer { - public: - ProfileOAuth2TokenService( - PrefService* user_prefs, - std::unique_ptr<OAuth2TokenServiceDelegate> delegate); - ~ProfileOAuth2TokenService() override; - - // Registers per-profile prefs. - static void RegisterProfilePrefs(PrefRegistrySimple* registry); - - void Shutdown(); - - // Loads credentials from a backing persistent store to make them available - // after service is used between profile restarts. - // - // The primary account is specified with the |primary_account_id| argument. - // For a regular profile, the primary account id comes from SigninManager. - // For a supervised user, the id comes from SupervisedUserService. - void LoadCredentials(const std::string& primary_account_id); - - // Returns true iff all credentials have been loaded from disk. - bool AreAllCredentialsLoaded(); - - // Returns true if LoadCredentials finished with no errors. - bool HasLoadCredentialsFinishedWithNoErrors(); - - // Updates a |refresh_token| for an |account_id|. Credentials are persisted, - // and available through |LoadCredentials| after service is restarted. - void UpdateCredentials( - const std::string& account_id, - const std::string& refresh_token, - signin_metrics::SourceForRefreshTokenOperation source = - signin_metrics::SourceForRefreshTokenOperation::kUnknown); - - void RevokeCredentials( - const std::string& account_id, - signin_metrics::SourceForRefreshTokenOperation source = - signin_metrics::SourceForRefreshTokenOperation::kUnknown); - - // Revokes all credentials. - void RevokeAllCredentials( - signin_metrics::SourceForRefreshTokenOperation source = - signin_metrics::SourceForRefreshTokenOperation::kUnknown); - - // Returns a pointer to its instance of net::BackoffEntry or nullptr if there - // is no such instance. - const net::BackoffEntry* GetDelegateBackoffEntry(); - -#if BUILDFLAG(ENABLE_DICE_SUPPORT) - // Removes the credentials associated to account_id from the internal storage, - // and moves them to |to_service|. The credentials are not revoked on the - // server, but the OnRefreshTokenRevoked() notification is sent to the - // observers. - void ExtractCredentials(ProfileOAuth2TokenService* to_service, - const std::string& account_id); -#endif - - void set_all_credentials_loaded_for_testing(bool loaded) { - all_credentials_loaded_ = loaded; - } - - // Exposes the ability to update auth errors to tests. - void UpdateAuthErrorForTesting(const std::string& account_id, - const GoogleServiceAuthError& error) { - UpdateAuthError(account_id, error); - } - - private: - friend class identity::IdentityManager; - - // OAuth2TokenService::Observer implementation. - void OnRefreshTokenAvailable(const std::string& account_id) override; - void OnRefreshTokenRevoked(const std::string& account_id) override; - void OnRefreshTokensLoaded() override; - - // Creates a new device ID if there are no accounts, or if the current device - // ID is empty. - void RecreateDeviceIdIfNeeded(); - - PrefService* user_prefs_; - - // Whether all credentials have been loaded. - bool all_credentials_loaded_; - - signin_metrics::SourceForRefreshTokenOperation update_refresh_token_source_ = - signin_metrics::SourceForRefreshTokenOperation::kUnknown; - - DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService); -}; - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ diff --git a/chromium/components/signin/core/browser/resources/OWNERS b/chromium/components/signin/core/browser/resources/OWNERS new file mode 100644 index 00000000000..c05db2e16e1 --- /dev/null +++ b/chromium/components/signin/core/browser/resources/OWNERS @@ -0,0 +1,2 @@ +# For trivial or mechanical horizontal JS/CSS/HTML changes. +file://ui/webui/PLATFORM_OWNERS diff --git a/chromium/components/signin/core/browser/resources/signin_index.html b/chromium/components/signin/core/browser/resources/signin_index.html index 64d66020dcb..b54d72bbf19 100644 --- a/chromium/components/signin/core/browser/resources/signin_index.html +++ b/chromium/components/signin/core/browser/resources/signin_index.html @@ -1,15 +1,15 @@ <!doctype html> -<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<html dir="ltr" lang="en"> <head> <meta charset="utf-8"> <script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/util.js"></script> <script src="chrome://resources/js/load_time_data.js"></script> <script src="strings.js"></script> - <if expr="is_ios"> - <!-- TODO(crbug.com/487000): Remove this once injected by the web layer. --> - <script src="chrome://resources/js/ios/web_ui.js"></script> - </if> +<if expr="is_ios"> + <!-- TODO(crbug.com/487000): Remove this once injected by the web layer. --> + <script src="chrome://resources/js/ios/web_ui.js"></script> +</if> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> <link rel="stylesheet" type="text/css" href="signin_index.css"> </head> diff --git a/chromium/components/signin/core/browser/signin_error_controller.cc b/chromium/components/signin/core/browser/signin_error_controller.cc index e3e4668f03e..bbfd519f5a0 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.cc +++ b/chromium/components/signin/core/browser/signin_error_controller.cc @@ -4,11 +4,11 @@ #include "components/signin/core/browser/signin_error_controller.h" -#include "components/signin/core/browser/signin_metrics.h" +#include "components/signin/public/base/signin_metrics.h" SigninErrorController::SigninErrorController( AccountMode mode, - identity::IdentityManager* identity_manager) + signin::IdentityManager* identity_manager) : account_mode_(mode), identity_manager_(identity_manager), scoped_identity_manager_observer_(this), diff --git a/chromium/components/signin/core/browser/signin_error_controller.h b/chromium/components/signin/core/browser/signin_error_controller.h index 311111985fe..b1f64862ecc 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.h +++ b/chromium/components/signin/core/browser/signin_error_controller.h @@ -13,15 +13,15 @@ #include "base/observer_list.h" #include "base/scoped_observer.h" #include "components/keyed_service/core/keyed_service.h" +#include "components/signin/public/identity_manager/identity_manager.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/cpp/identity_manager.h" // Keep track of auth errors and expose them to observers in the UI. Services // that wish to expose auth errors to the user should register an // AuthStatusProvider to report their current authentication state, and should // invoke AuthStatusChanged() when their authentication state may have changed. class SigninErrorController : public KeyedService, - public identity::IdentityManager::Observer { + public signin::IdentityManager::Observer { public: enum class AccountMode { // Signin error controller monitors all the accounts. When multiple accounts @@ -42,7 +42,7 @@ class SigninErrorController : public KeyedService, }; SigninErrorController(AccountMode mode, - identity::IdentityManager* identity_manager); + signin::IdentityManager* identity_manager); ~SigninErrorController() override; // KeyedService implementation: @@ -75,7 +75,7 @@ class SigninErrorController : public KeyedService, const std::string& prev_account_id, const GoogleServiceAuthError::State& prev_error_state); - // identity::IdentityManager::Observer: + // signin::IdentityManager::Observer: void OnEndBatchOfRefreshTokenStateChanges() override; void OnErrorStateOfRefreshTokenUpdatedForAccount( const CoreAccountInfo& account_info, @@ -86,9 +86,9 @@ class SigninErrorController : public KeyedService, const CoreAccountInfo& previous_primary_account_info) override; const AccountMode account_mode_; - identity::IdentityManager* identity_manager_; + signin::IdentityManager* identity_manager_; - ScopedObserver<identity::IdentityManager, SigninErrorController> + ScopedObserver<signin::IdentityManager, SigninErrorController> scoped_identity_manager_observer_; // The account that generated the last auth error. 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 90b353983bc..92b478fb49d 100644 --- a/chromium/components/signin/core/browser/signin_error_controller_unittest.cc +++ b/chromium/components/signin/core/browser/signin_error_controller_unittest.cc @@ -12,8 +12,8 @@ #include "base/scoped_observer.h" #include "base/stl_util.h" #include "base/test/scoped_task_environment.h" -#include "services/identity/public/cpp/identity_test_environment.h" -#include "services/identity/public/cpp/primary_account_mutator.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "components/signin/public/identity_manager/primary_account_mutator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -36,7 +36,7 @@ TEST(SigninErrorControllerTest, SingleAccount) { EXPECT_CALL(observer, OnErrorChanged()).Times(0); base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; SigninErrorController error_controller( SigninErrorController::AccountMode::ANY_ACCOUNT, identity_test_env.identity_manager()); @@ -65,7 +65,7 @@ TEST(SigninErrorControllerTest, SingleAccount) { ::testing::Mock::VerifyAndClearExpectations(&observer); GoogleServiceAuthError error2 = - GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED); + GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP); EXPECT_CALL(observer, OnErrorChanged()).Times(1); identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount( test_account_id, error2); @@ -84,7 +84,7 @@ TEST(SigninErrorControllerTest, SingleAccount) { TEST(SigninErrorControllerTest, AccountTransitionAnyAccount) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; std::string test_account_id = identity_test_env.MakeAccountAvailable(kTestEmail).account_id; @@ -115,8 +115,8 @@ TEST(SigninErrorControllerTest, AccountTransitionAnyAccount) { #if !defined(OS_CHROMEOS) TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; - identity::PrimaryAccountMutator* primary_account_mutator = + signin::IdentityTestEnvironment identity_test_env; + signin::PrimaryAccountMutator* primary_account_mutator = identity_test_env.identity_manager()->GetPrimaryAccountMutator(); std::string test_account_id = @@ -144,7 +144,7 @@ TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) { // Change the primary account to the account with an error and check that the // error controller updates its error status accordingly. primary_account_mutator->ClearPrimaryAccount( - identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, + signin::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST, signin_metrics::SignoutDelete::IGNORE_METRIC); identity_test_env.SetPrimaryAccount(kTestEmail); @@ -160,7 +160,7 @@ TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) { // Change the primary account again and check that the error controller // updates its error status accordingly. primary_account_mutator->ClearPrimaryAccount( - identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, + signin::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST, signin_metrics::SignoutDelete::IGNORE_METRIC); identity_test_env.SetPrimaryAccount(kOtherTestEmail); @@ -170,7 +170,7 @@ TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) { // Sign out and check that that the error controller updates its error status // accordingly. primary_account_mutator->ClearPrimaryAccount( - identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, + signin::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST, signin_metrics::SignoutDelete::IGNORE_METRIC); ASSERT_FALSE(error_controller.HasError()); @@ -180,7 +180,7 @@ TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) { // Verify that SigninErrorController handles errors properly. TEST(SigninErrorControllerTest, AuthStatusEnumerateAllErrors) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; std::string test_account_id = identity_test_env.MakeAccountAvailable(kTestEmail).account_id; @@ -193,23 +193,16 @@ TEST(SigninErrorControllerTest, AuthStatusEnumerateAllErrors) { GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, GoogleServiceAuthError::USER_NOT_SIGNED_UP, GoogleServiceAuthError::CONNECTION_FAILED, - GoogleServiceAuthError::CAPTCHA_REQUIRED, - GoogleServiceAuthError::ACCOUNT_DELETED, - GoogleServiceAuthError::ACCOUNT_DISABLED, GoogleServiceAuthError::SERVICE_UNAVAILABLE, - GoogleServiceAuthError::TWO_FACTOR, GoogleServiceAuthError::REQUEST_CANCELED, - GoogleServiceAuthError::HOSTED_NOT_ALLOWED_DEPRECATED, GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE, - GoogleServiceAuthError::SERVICE_ERROR, - GoogleServiceAuthError::WEB_LOGIN_REQUIRED}; - static_assert(base::size(table) == GoogleServiceAuthError::NUM_STATES, - "table array does not match the number of auth error types"); + GoogleServiceAuthError::SERVICE_ERROR}; + static_assert( + base::size(table) == GoogleServiceAuthError::NUM_STATES - + GoogleServiceAuthError::kDeprecatedStateCount, + "table array does not match the number of auth error types"); for (GoogleServiceAuthError::State state : table) { - if (GoogleServiceAuthError::IsDeprecated(state)) - continue; - GoogleServiceAuthError error(state); if (error.IsTransientError()) @@ -234,7 +227,7 @@ TEST(SigninErrorControllerTest, AuthStatusEnumerateAllErrors) { // Verify that existing error is not replaced by new error. TEST(SigninErrorControllerTest, AuthStatusChange) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; std::string test_account_id = identity_test_env.MakeAccountAvailable(kTestEmail).account_id; @@ -290,7 +283,7 @@ TEST(SigninErrorControllerTest, AuthStatusChange) { TEST(SigninErrorControllerTest, PrimaryAccountErrorsArePreferredToSecondaryAccountErrors) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; AccountInfo primary_account_info = identity_test_env.MakePrimaryAccountAvailable(kPrimaryAccountEmail); @@ -337,7 +330,7 @@ TEST(SigninErrorControllerTest, TEST(SigninErrorControllerTest, PrimaryAccountErrorsAreSticky) { base::test::ScopedTaskEnvironment task_environment; - identity::IdentityTestEnvironment identity_test_env; + signin::IdentityTestEnvironment identity_test_env; AccountInfo primary_account_info = identity_test_env.MakePrimaryAccountAvailable(kPrimaryAccountEmail); diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc index 3be4ed58894..cf0a3877b3e 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.cc +++ b/chromium/components/signin/core/browser/signin_header_helper.cc @@ -22,16 +22,6 @@ namespace signin { -namespace { -// Buckes of the |Signin.RequestHeaderOperation.Dice| and -// |SigninRequestHeaderOperation.Mirror| histograms. -enum class RequestHeaderOperation { - kHeaderAdded = 0, - kHeaderRemoved = 1, - kMaxValue = kHeaderRemoved -}; -} // namespace - const char kChromeConnectedHeader[] = "X-Chrome-Connected"; const char kDiceRequestHeader[] = "X-Chrome-ID-Consistency-Request"; const char kDiceResponseHeader[] = "X-Chrome-ID-Consistency-Response"; @@ -106,8 +96,7 @@ std::string BuildMirrorRequestCookieIfPossible( url, account_id, account_consistency, cookie_settings, profile_mode_mask); } -SigninHeaderHelper::SigninHeaderHelper(const std::string& histogram_suffix) - : histogram_suffix_(histogram_suffix) {} +SigninHeaderHelper::SigninHeaderHelper() = default; SigninHeaderHelper::~SigninHeaderHelper() = default; bool SigninHeaderHelper::AppendOrRemoveRequestHeader( @@ -122,16 +111,12 @@ bool SigninHeaderHelper::AppendOrRemoveRequestHeader( if (!redirect_url.is_empty() && request->HasHeader(header_name) && IsUrlEligibleForRequestHeader(request->GetUrl()) && !IsUrlEligibleForRequestHeader(redirect_url)) { - base::UmaHistogramEnumeration( - GetSuffixedHistogramName("Signin.RequestHeaderOperation"), - RequestHeaderOperation::kHeaderRemoved); + VLOG(1) << "Sign-in request header [" << header_name << "] removed."; request->RemoveRequestHeaderByName(header_name); } return false; } - base::UmaHistogramEnumeration( - GetSuffixedHistogramName("Signin.RequestHeaderOperation"), - RequestHeaderOperation::kHeaderAdded); + VLOG(1) << "Sign-in request header [" << header_name << "] added."; request->SetExtraHeaderByName(header_name, header_value); return true; } @@ -159,11 +144,6 @@ SigninHeaderHelper::ParseAccountConsistencyResponseHeader( return dictionary; } -std::string SigninHeaderHelper::GetSuffixedHistogramName( - const std::string& histogram_name) { - return histogram_name + "." + histogram_suffix_; -} - void AppendOrRemoveMirrorRequestHeader( RequestAdapter* request, const GURL& redirect_url, diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h index ab001f6592d..ac718de9f54 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.h +++ b/chromium/components/signin/core/browser/signin_header_helper.h @@ -10,8 +10,8 @@ #include <vector> #include "components/prefs/pref_member.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/signin_buildflags.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_buildflags.h" #include "url/gurl.h" namespace content_settings { @@ -42,7 +42,7 @@ extern const char kDiceResponseHeader[]; // perform. // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.signin -enum GAIAServiceType { +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. @@ -181,7 +181,7 @@ class SigninHeaderHelper { const content_settings::CookieSettings* cookie_settings) = 0; protected: - explicit SigninHeaderHelper(const std::string& histogram_suffix); + SigninHeaderHelper(); virtual ~SigninHeaderHelper(); // Dictionary of fields in a account consistency response header. @@ -196,13 +196,6 @@ class SigninHeaderHelper { // Returns whether the url is eligible for the request header. virtual bool IsUrlEligibleForRequestHeader(const GURL& url) = 0; - // Returns a string that can be used as a histogram name. Its value ios - // "|histogram_name|.|histogram_suffix_|". - std::string GetSuffixedHistogramName(const std::string& histogram_name); - - // Suffix to be used by the histograms recodered by this SigninHeaderHelper. - std::string histogram_suffix_; - DISALLOW_COPY_AND_ASSIGN(SigninHeaderHelper); }; 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 61487c836db..c36fd26f945 100644 --- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc @@ -14,9 +14,9 @@ #include "build/build_config.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/prefs/pref_member.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/chrome_connected_header_helper.h" -#include "components/signin/core/browser/signin_buildflags.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_buildflags.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "google_apis/gaia/gaia_urls.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" diff --git a/chromium/components/signin/core/browser/signin_investigator.cc b/chromium/components/signin/core/browser/signin_investigator.cc index 7dc1236ecc2..3c77124bd15 100644 --- a/chromium/components/signin/core/browser/signin_investigator.cc +++ b/chromium/components/signin/core/browser/signin_investigator.cc @@ -6,21 +6,13 @@ #include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" #include "google_apis/gaia/gaia_auth_util.h" using signin_metrics::AccountEquality; using signin_metrics::LogAccountEquality; -namespace { -void LogSigninScenario(InvestigatedScenario scenario) { - UMA_HISTOGRAM_ENUMERATION( - "Signin.InvestigatedScenario", static_cast<int>(scenario), - static_cast<int>(InvestigatedScenario::NUM_ENTRIES)); -} -} // namespace - SigninInvestigator::SigninInvestigator(const std::string& current_email, const std::string& current_id, DependencyProvider* provider) @@ -62,19 +54,13 @@ InvestigatedScenario SigninInvestigator::Investigate() { if (provider_->GetPrefs() ->GetString(prefs::kGoogleServicesLastUsername) .empty()) { - scenario = IsUpgradeHighRisk() ? InvestigatedScenario::UPGRADE_HIGH_RISK - : InvestigatedScenario::UPGRADE_LOW_RISK; + scenario = InvestigatedScenario::kFirstSignIn; } else if (AreAccountsEqualWithFallback()) { - scenario = InvestigatedScenario::SAME_ACCOUNT; + scenario = InvestigatedScenario::kSameAccount; } else { - scenario = InvestigatedScenario::DIFFERENT_ACCOUNT; + scenario = InvestigatedScenario::kDifferentAccount; } - LogSigninScenario(scenario); + UMA_HISTOGRAM_ENUMERATION("Signin.InvestigatedScenario", scenario); return scenario; } - -bool SigninInvestigator::IsUpgradeHighRisk() { - // TODO(skym): Add logic to make this decision, crbug.com/572754. - return false; -} diff --git a/chromium/components/signin/core/browser/signin_investigator.h b/chromium/components/signin/core/browser/signin_investigator.h index ce40682bfda..301e9f9eb50 100644 --- a/chromium/components/signin/core/browser/signin_investigator.h +++ b/chromium/components/signin/core/browser/signin_investigator.h @@ -10,23 +10,24 @@ #include "base/macros.h" #include "components/prefs/pref_service.h" +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.signin // Broad categorization of signin type from investigation. enum class InvestigatedScenario : int { // First signin and should not be warned. As little friction as possible // should get between the user and signing in. - UPGRADE_LOW_RISK = 0, - // First signin but should be warned. There is a reason to believe this signin - // may not be what the user wanted. - UPGRADE_HIGH_RISK, + kFirstSignIn = 0, + // Was never used (see crbug.com/983183, crbug.com/572754). + kDeprecatedUpgradeHighRisk = 1, // Relogging with the same account. - SAME_ACCOUNT, + kSameAccount = 2, // User is switching accounts, can be very dangerous depending on the amount // of local syncable data. - DIFFERENT_ACCOUNT, + kDifferentAccount = 3, // Always the last enumerated type. - NUM_ENTRIES, + kMaxValue = kDifferentAccount, }; // Performs various checks with the current account being used to login in and @@ -64,12 +65,6 @@ class SigninInvestigator { // slightly more strict than the version AccountId defines as == operator. bool AreAccountsEqualWithFallback(); - // Determines if the current upgrade signin is high risk or not. Reasons for - // being high risk may include things like this profile/device may be shared - // among multiple people, or there may be old syncable data created by other - // users that do not currently use the profile/device. - bool IsUpgradeHighRisk(); - std::string current_email_; std::string current_id_; diff --git a/chromium/components/signin/core/browser/signin_investigator_unittest.cc b/chromium/components/signin/core/browser/signin_investigator_unittest.cc index cb861a03128..31df9131e47 100644 --- a/chromium/components/signin/core/browser/signin_investigator_unittest.cc +++ b/chromium/components/signin/core/browser/signin_investigator_unittest.cc @@ -8,10 +8,10 @@ #include "base/test/scoped_task_environment.h" #include "components/prefs/pref_registry_simple.h" #include "components/signin/core/browser/signin_investigator.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/sync_preferences/testing_pref_service_syncable.h" -#include "services/identity/public/cpp/identity_test_environment.h" #include "testing/gtest/include/gtest/gtest.h" using signin_metrics::AccountEquality; @@ -35,7 +35,7 @@ class FakeProvider : public SigninInvestigator::DependencyProvider { private: base::test::ScopedTaskEnvironment task_environment_; sync_preferences::TestingPrefServiceSyncable prefs_; - identity::IdentityTestEnvironment identity_test_env_; + signin::IdentityTestEnvironment identity_test_env_; }; } // namespace @@ -116,14 +116,14 @@ TEST_F(SigninInvestigatorTest, EqualityDifferentEmailFallbackEmptyCurrentId) { TEST_F(SigninInvestigatorTest, InvestigateSameAccount) { AssertInvestigatedScenario(kSameEmail, kSameId, - InvestigatedScenario::SAME_ACCOUNT); + InvestigatedScenario::kSameAccount); } -TEST_F(SigninInvestigatorTest, InvestigateUpgrade) { - AssertInvestigatedScenario("", "", InvestigatedScenario::UPGRADE_LOW_RISK); +TEST_F(SigninInvestigatorTest, InvestigateFirstSignIn) { + AssertInvestigatedScenario("", "", InvestigatedScenario::kFirstSignIn); } TEST_F(SigninInvestigatorTest, InvestigateDifferentAccount) { AssertInvestigatedScenario(kDifferentEmail, kDifferentId, - InvestigatedScenario::DIFFERENT_ACCOUNT); + InvestigatedScenario::kDifferentAccount); } diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc deleted file mode 100644 index d97a3e1c54a..00000000000 --- a/chromium/components/signin/core/browser/signin_manager.cc +++ /dev/null @@ -1,178 +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. - -#include "components/signin/core/browser/signin_manager.h" - -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "components/prefs/pref_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/identity_utils.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "google_apis/gaia/gaia_auth_util.h" -#include "google_apis/gaia/gaia_constants.h" -#include "google_apis/gaia/gaia_urls.h" -#include "google_apis/gaia/google_service_auth_error.h" -#include "third_party/icu/source/i18n/unicode/regex.h" - -SigninManager::SigninManager( - SigninClient* client, - ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service, - GaiaCookieManagerService* cookie_manager_service, - signin::AccountConsistencyMethod account_consistency) - : SigninManagerBase(client, - token_service, - account_tracker_service, - cookie_manager_service, - account_consistency), - weak_pointer_factory_(this) {} - -SigninManager::~SigninManager() { - token_service()->RemoveObserver(this); - local_state_pref_registrar_.RemoveAll(); -} - -void SigninManager::FinalizeInitBeforeLoadingRefreshTokens( - PrefService* local_state) { - // local_state can be null during unit tests. - if (local_state) { - local_state_pref_registrar_.Init(local_state); - local_state_pref_registrar_.Add( - prefs::kGoogleServicesUsernamePattern, - base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged, - weak_pointer_factory_.GetWeakPtr())); - } - signin_allowed_.Init(prefs::kSigninAllowed, signin_client()->GetPrefs(), - base::Bind(&SigninManager::OnSigninAllowedPrefChanged, - base::Unretained(this))); - - AccountInfo account_info = GetAuthenticatedAccountInfo(); - if (!account_info.account_id.empty() && - (!IsAllowedUsername(account_info.email) || !IsSigninAllowed())) { - // User is signed in, but the username is invalid or signin is no longer - // allowed, so the user must be sign out. - // - // This may happen in the following cases: - // a. The user has toggled off signin allowed in settings. - // b. The administrator changed the policy since the last signin. - // - // Note: The token service has not yet loaded its credentials, so accounts - // cannot be revoked here. - // - // On desktop, when SigninManager is initializing, the profile was not yet - // marked with sign out allowed. Therefore sign out is not allowed and all - // calls to SignOut methods are no-op. - // - // TODO(msarda): SignOut methods do not gurantee that sign out can actually - // be done (this depends on whether sign out is allowed). Add a check here - // on desktop to make it clear that SignOut does not do anything. - SignOutAndKeepAllAccounts(signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN, - signin_metrics::SignoutDelete::IGNORE_METRIC); - } - - // It is important to only load credentials after starting to observe the - // token service. - token_service()->AddObserver(this); -} - -void SigninManager::OnGoogleServicesUsernamePatternChanged() { - if (IsAuthenticated() && - !IsAllowedUsername(GetAuthenticatedAccountInfo().email)) { - // Signed in user is invalid according to the current policy so sign - // the user out. - SignOut(signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED, - signin_metrics::SignoutDelete::IGNORE_METRIC); - } -} - -bool SigninManager::IsSigninAllowed() const { - return signin_allowed_.GetValue(); -} - -void SigninManager::SetSigninAllowed(bool allowed) { - signin_allowed_.SetValue(allowed); -} - -void SigninManager::OnSigninAllowedPrefChanged() { - if (!IsSigninAllowed() && IsAuthenticated()) { - VLOG(0) << "IsSigninAllowed() set to false, signing out the user"; - SignOut(signin_metrics::SIGNOUT_PREF_CHANGED, - signin_metrics::SignoutDelete::IGNORE_METRIC); - } -} - -// static -SigninManager* SigninManager::FromSigninManagerBase( - SigninManagerBase* manager) { - return static_cast<SigninManager*>(manager); -} - -bool SigninManager::IsAllowedUsername(const std::string& username) const { - const PrefService* local_state = local_state_pref_registrar_.prefs(); - if (!local_state) - return true; // In a unit test with no local state - all names are allowed. - - std::string pattern = - local_state->GetString(prefs::kGoogleServicesUsernamePattern); - return identity::IsUsernameAllowedByPattern(username, pattern); -} - -void SigninManager::SignIn(const std::string& username) { - AccountInfo info = - account_tracker_service()->FindAccountInfoByEmail(username); - DCHECK(!info.gaia.empty()); - DCHECK(!info.email.empty()); - - bool reauth_in_progress = IsAuthenticated(); - - signin_client()->GetPrefs()->SetInt64( - prefs::kSignedInTime, - base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds()); - - SetAuthenticatedAccountInfo(info.gaia, info.email); - - if (!reauth_in_progress) - FireGoogleSigninSucceeded(); - - signin_metrics::LogSigninProfile(signin_client()->IsFirstRun(), - signin_client()->GetInstallDate()); -} - -void SigninManager::FireGoogleSigninSucceeded() { - const AccountInfo account_info = GetAuthenticatedAccountInfo(); - if (observer_ != nullptr) { - observer_->GoogleSigninSucceeded(account_info); - } -} - -void SigninManager::OnRefreshTokensLoaded() { - token_service()->RemoveObserver(this); - - if (account_tracker_service()->GetMigrationState() == - AccountTrackerService::MIGRATION_IN_PROGRESS) { - account_tracker_service()->SetMigrationDone(); - } - - // Remove account information from the account tracker service if needed. - if (token_service()->HasLoadCredentialsFinishedWithNoErrors()) { - std::vector<AccountInfo> accounts_in_tracker_service = - account_tracker_service()->GetAccounts(); - for (const auto& account : accounts_in_tracker_service) { - if (GetAuthenticatedAccountId() != account.account_id && - !token_service()->RefreshTokenIsAvailable(account.account_id)) { - DVLOG(0) << "Removed account from account tracker service: " - << account.account_id; - account_tracker_service()->RemoveAccount(account.account_id); - } - } - } -} diff --git a/chromium/components/signin/core/browser/signin_manager.h b/chromium/components/signin/core/browser/signin_manager.h deleted file mode 100644 index c70c9d93b6b..00000000000 --- a/chromium/components/signin/core/browser/signin_manager.h +++ /dev/null @@ -1,118 +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. -// -// The signin manager encapsulates some functionality tracking -// which user is signed in. See SigninManagerBase for full description of -// responsibilities. The class defined in this file provides functionality -// required by all platforms except Chrome OS. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_H_ - -#include "build/build_config.h" - -#if defined(OS_CHROMEOS) - -#include "components/signin/core/browser/signin_manager_base.h" - -#else - -#include <set> -#include <string> - -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/observer_list.h" -#include "base/strings/string_piece.h" -#include "components/keyed_service/core/keyed_service.h" -#include "components/prefs/pref_change_registrar.h" -#include "components/prefs/pref_member.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_manager_base.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "net/cookies/canonical_cookie.h" - -class PrefService; - -namespace identity { -class IdentityManager; -} // namespace identity - -class SigninManager : public SigninManagerBase, - public OAuth2TokenService::Observer { - public: - // This is used to distinguish URLs belonging to the special web signin flow - // running in the special signin process from other URLs on the same domain. - // We do not grant WebUI privilieges / bindings to this process or to URLs of - // this scheme; enforcement of privileges is handled separately by - // OneClickSigninHelper. - static const char kChromeSigninEffectiveSite[]; - - SigninManager(SigninClient* client, - ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service, - GaiaCookieManagerService* cookie_manager_service, - signin::AccountConsistencyMethod account_consistency); - ~SigninManager() override; - - // Returns |manager| as a SigninManager instance. Relies on the fact that on - // platforms where signin_manager.* is built, all SigninManagerBase instances - // are actually SigninManager instances. - static SigninManager* FromSigninManagerBase(SigninManagerBase* manager); - - // On platforms where SigninManager is responsible for dealing with - // invalid username policy updates, we need to check this during - // initialization and sign the user out. - void FinalizeInitBeforeLoadingRefreshTokens( - PrefService* local_state) override; - - // Signs a user in. SigninManager assumes that |username| can be used to look - // up the corresponding account_id and gaia_id for this email. - void SignIn(const std::string& username); - - // Returns true if a signin to Chrome is allowed (by policy or pref). - // TODO(crbug.com/806778): this method should not be used externally, - // instead the value of the kSigninAllowed preference should be checked. - // Once all external code has been modified, this method will be removed. - bool IsSigninAllowed() const; - - // Sets whether sign-in is allowed or not. - void SetSigninAllowed(bool allowed); - - private: - friend class identity::IdentityManager; - FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, Prohibited); - FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, TestAlternateWildcard); - - // Send all observers |GoogleSigninSucceeded| notifications. - void FireGoogleSigninSucceeded(); - - // OAuth2TokenService::Observer: - void OnRefreshTokensLoaded() override; - - void OnSigninAllowedPrefChanged(); - void OnGoogleServicesUsernamePatternChanged(); - - // Returns true if the passed username is allowed by policy. - bool IsAllowedUsername(const std::string& username) const; - - // Helper object to listen for changes to signin preferences stored in non- - // profile-specific local prefs (like kGoogleServicesUsernamePattern). - PrefChangeRegistrar local_state_pref_registrar_; - - // Helper object to listen for changes to the signin allowed preference. - BooleanPrefMember signin_allowed_; - - base::WeakPtrFactory<SigninManager> weak_pointer_factory_; - - DISALLOW_COPY_AND_ASSIGN(SigninManager); -}; - -#endif // !defined(OS_CHROMEOS) - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_H_ diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc index 5bc15bf236d..279eab33886 100644 --- a/chromium/components/signin/core/browser/signin_status_metrics_provider.cc +++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.cc @@ -18,8 +18,7 @@ SigninStatusMetricsProvider::SigninStatusMetricsProvider( bool is_test) : delegate_(std::move(delegate)), scoped_observer_(this), - is_test_(is_test), - weak_ptr_factory_(this) { + is_test_(is_test) { DCHECK(delegate_ || is_test_); if (is_test_) return; @@ -53,7 +52,7 @@ SigninStatusMetricsProvider::CreateInstance( } void SigninStatusMetricsProvider::OnIdentityManagerCreated( - identity::IdentityManager* identity_manager) { + signin::IdentityManager* identity_manager) { // Whenever a new profile is created, a new IdentityManager will be created // for it. This ensures that all sign-in or sign-out actions of all opened // profiles are being monitored. @@ -69,7 +68,7 @@ void SigninStatusMetricsProvider::OnIdentityManagerCreated( } void SigninStatusMetricsProvider::OnIdentityManagerShutdown( - identity::IdentityManager* identity_manager) { + signin::IdentityManager* identity_manager) { if (scoped_observer_.IsObserving(identity_manager)) scoped_observer_.Remove(identity_manager); } @@ -102,7 +101,7 @@ void SigninStatusMetricsProvider::Initialize() { delegate_->Initialize(); // Start observing all already-created IdentityManagers. - for (identity::IdentityManager* manager : + for (signin::IdentityManager* manager : delegate_->GetIdentityManagersForAllAccounts()) { DCHECK(!scoped_observer_.IsObserving(manager)); scoped_observer_.Add(manager); diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider.h b/chromium/components/signin/core/browser/signin_status_metrics_provider.h index 1a027dcc951..f28f472ffc6 100644 --- a/chromium/components/signin/core/browser/signin_status_metrics_provider.h +++ b/chromium/components/signin/core/browser/signin_status_metrics_provider.h @@ -16,7 +16,7 @@ #include "build/build_config.h" #include "components/signin/core/browser/signin_status_metrics_provider_base.h" #include "components/signin/core/browser/signin_status_metrics_provider_delegate.h" -#include "services/identity/public/cpp/identity_manager.h" +#include "components/signin/public/identity_manager/identity_manager.h" namespace metrics { class ChromeUserMetricsExtension; @@ -28,7 +28,7 @@ class SigninStatusMetricsProviderDelegate; // record the value into a histogram before UMA log is uploaded on platform // Windows, Linux, Mac and Android. class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase, - public identity::IdentityManager::Observer { + public signin::IdentityManager::Observer { public: ~SigninStatusMetricsProvider() override; @@ -41,10 +41,10 @@ class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase, std::unique_ptr<SigninStatusMetricsProviderDelegate> delegate); // Update the sign-in status when a IdentityManager is created. - void OnIdentityManagerCreated(identity::IdentityManager* identity_manager); + void OnIdentityManagerCreated(signin::IdentityManager* identity_manager); // Update the sign-in status when a IdentityManager is shut down. - void OnIdentityManagerShutdown(identity::IdentityManager* identity_manager); + void OnIdentityManagerShutdown(signin::IdentityManager* identity_manager); // Updates the initial sign-in status. For testing purpose only. void UpdateInitialSigninStatusForTesting(size_t total_count, @@ -90,13 +90,13 @@ class SigninStatusMetricsProvider : public SigninStatusMetricsProviderBase, // Used to track the IdentityManagers that this instance is observing so that // this instance can be removed as an observer on its destruction. - ScopedObserver<identity::IdentityManager, identity::IdentityManager::Observer> + ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer> scoped_observer_; // Whether the instance is for testing or not. bool is_test_; - base::WeakPtrFactory<SigninStatusMetricsProvider> weak_ptr_factory_; + base::WeakPtrFactory<SigninStatusMetricsProvider> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SigninStatusMetricsProvider); }; diff --git a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h b/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h index 763a8b7ccda..b97ee435691 100644 --- a/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h +++ b/chromium/components/signin/core/browser/signin_status_metrics_provider_delegate.h @@ -14,7 +14,7 @@ class SigninStatusMetricsProvider; -namespace identity { +namespace signin { class IdentityManager; } @@ -48,7 +48,7 @@ class SigninStatusMetricsProviderDelegate { virtual AccountsStatus GetStatusOfAllAccounts() = 0; // Returns the IdentityManager instance (if any) associated with each account. - virtual std::vector<identity::IdentityManager*> + virtual std::vector<signin::IdentityManager*> GetIdentityManagersForAllAccounts() = 0; protected: diff --git a/chromium/components/signin/internal/identity_manager/BUILD.gn b/chromium/components/signin/internal/identity_manager/BUILD.gn new file mode 100644 index 00000000000..40caa1eccaf --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/BUILD.gn @@ -0,0 +1,188 @@ +# 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. + +# This target forms the core of the IdentityManager implementation +# (//components/signin/public/identity_manager/identity_manager.*). +source_set("identity_manager") { + sources = [ + "account_fetcher_service.cc", + "account_fetcher_service.h", + "account_info_fetcher.cc", + "account_info_fetcher.h", + "account_info_util.cc", + "account_info_util.h", + "account_tracker_service.cc", + "account_tracker_service.h", + "accounts_cookie_mutator_impl.cc", + "accounts_cookie_mutator_impl.h", + "child_account_info_fetcher_android.cc", + "child_account_info_fetcher_android.h", + "diagnostics_provider_impl.cc", + "diagnostics_provider_impl.h", + "gaia_cookie_manager_service.cc", + "gaia_cookie_manager_service.h", + "mutable_profile_oauth2_token_service_delegate.cc", + "mutable_profile_oauth2_token_service_delegate.h", + "oauth2_token_service_delegate_android.cc", + "oauth2_token_service_delegate_android.h", + "oauth_multilogin_helper.cc", + "oauth_multilogin_helper.h", + "oauth_multilogin_token_fetcher.cc", + "oauth_multilogin_token_fetcher.h", + "primary_account_manager.cc", + "primary_account_manager.h", + "primary_account_mutator_impl.cc", + "primary_account_mutator_impl.h", + "primary_account_policy_manager.h", + "profile_oauth2_token_service.cc", + "profile_oauth2_token_service.h", + "profile_oauth2_token_service_builder.cc", + "profile_oauth2_token_service_builder.h", + "profile_oauth2_token_service_delegate.cc", + "profile_oauth2_token_service_delegate.h", + "profile_oauth2_token_service_delegate_chromeos.cc", + "profile_oauth2_token_service_delegate_chromeos.h", + "profile_oauth2_token_service_delegate_ios.h", + "profile_oauth2_token_service_delegate_ios.mm", + "ubertoken_fetcher_impl.cc", + "ubertoken_fetcher_impl.h", + ] + + configs += [ "//build/config/compiler:wexit_time_destructors" ] + + public_deps = [ + "//base", + "//components/signin/public/base", + "//components/signin/public/base:signin_buildflags", + "//components/webdata/common", + "//google_apis", + "//mojo/public/cpp/bindings", + "//net", + "//services/network/public/mojom", + ] + + deps = [ + "//components/image_fetcher/core", + "//components/prefs", + "//components/signin/public/webdata", + "//services/network/public/cpp", + "//ui/gfx", + ] + + if (is_android) { + deps += [ "//components/signin/core/browser/android:jni_headers" ] + } + + if (is_chromeos) { + public_deps += [ "//chromeos/components/account_manager" ] + + deps += [ + "//chromeos/constants", + "//components/user_manager", + ] + } else { + sources += [ + "primary_account_policy_manager_impl.cc", + "primary_account_policy_manager_impl.h", + ] + + public_deps += [ "//components/prefs" ] + } + + if (!is_android && !is_ios) { + sources += [ + "accounts_mutator_impl.cc", + "accounts_mutator_impl.h", + ] + } + + if (is_ios) { + configs += [ "//build/config/compiler:enable_arc" ] + + sources += [ + "device_accounts_synchronizer_impl.cc", + "device_accounts_synchronizer_impl.h", + ] + + deps += [ "//components/signin/public/identity_manager/ios" ] + } +} + +source_set("unit_tests") { + testonly = true + + sources = [ + "account_info_util_unittest.cc", + "account_tracker_service_unittest.cc", + "gaia_cookie_manager_service_unittest.cc", + "mutable_profile_oauth2_token_service_delegate_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_chromeos_unittest.cc", + "profile_oauth2_token_service_delegate_ios_unittest.mm", + "profile_oauth2_token_service_delegate_unittest.cc", + "profile_oauth2_token_service_unittest.cc", + "ubertoken_fetcher_impl_unittest.cc", + ] + + deps = [ + ":identity_manager", + ":test_support", + "//base", + "//base/test:test_support", + "//components/image_fetcher/core", + "//components/image_fetcher/core:test_support", + "//components/os_crypt:test_support", + "//components/prefs", + "//components/prefs:test_support", + "//components/signin/public/base", + "//components/signin/public/base:signin_buildflags", + "//components/signin/public/base:test_support", + "//components/signin/public/identity_manager", + "//components/signin/public/webdata", + "//components/sync_preferences:test_support", + "//components/webdata/common", + "//google_apis", + "//net", + "//net:test_support", + "//services/network:test_support", + "//services/network/public/cpp", + "//testing/gmock", + "//testing/gtest", + ] + + if (is_chromeos) { + sources -= [ "primary_account_policy_manager_impl_unittest.cc" ] + + deps += [ "//chromeos/components/account_manager" ] + } + + if (is_ios) { + configs += [ "//build/config/compiler:enable_arc" ] + + deps += [ "//components/signin/public/identity_manager/ios:test_support" ] + } +} + +# This target contains test support that backs the test support for +# IdentityManager. +static_library("test_support") { + testonly = true + + sources = [ + "fake_profile_oauth2_token_service.cc", + "fake_profile_oauth2_token_service.h", + "fake_profile_oauth2_token_service_delegate.cc", + "fake_profile_oauth2_token_service_delegate.h", + ] + + public_deps = [ + ":identity_manager", + "//base", + "//google_apis:test_support", + ] +} diff --git a/chromium/components/signin/internal/identity_manager/DEPS b/chromium/components/signin/internal/identity_manager/DEPS new file mode 100644 index 00000000000..4485df3b6a5 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/DEPS @@ -0,0 +1,16 @@ +include_rules = [ + "+chromeos/constants/chromeos_switches.h", + "+mojo/public", +] + +specific_include_rules = { + "account_tracker_service.cc": [ + "+components/signin/core/browser/android/jni_headers/AccountTrackerService_jni.h", + ], + "child_account_info_fetcher_android.cc": [ + "+components/signin/core/browser/android/jni_headers/ChildAccountInfoFetcher_jni.h", + ], + "oauth2_token_service_delegate_android.cc": [ + "+components/signin/core/browser/android/jni_headers/OAuth2TokenService_jni.h", + ], +} diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/internal/identity_manager/account_fetcher_service.cc index 6ff6a0421b4..97889b99ee8 100644 --- a/chromium/components/signin/core/browser/account_fetcher_service.cc +++ b/chromium/components/signin/internal/identity_manager/account_fetcher_service.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/core/browser/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" #include <string> #include <utility> @@ -17,16 +17,16 @@ #include "components/image_fetcher/core/image_fetcher_impl.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/account_info_fetcher.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/avatar_icon_util.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_switches.h" +#include "components/signin/internal/identity_manager/account_info_fetcher.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/public/base/avatar_icon_util.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_switches.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #if defined(OS_ANDROID) -#include "components/signin/core/browser/child_account_info_fetcher_android.h" +#include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h" #endif namespace { @@ -34,12 +34,12 @@ namespace { const base::TimeDelta kRefreshFromTokenServiceDelay = base::TimeDelta::FromHours(24); -bool AccountSupportsUserInfo(const std::string& account_id) { +bool AccountSupportsUserInfo(const CoreAccountId& account_id) { // Supervised users use a specially scoped token which when used for general // purposes causes the token service to raise spurious auth errors. // TODO(treib): this string is also used in supervised_user_constants.cc. // Should put in a common place. - return account_id != "managed_user@localhost"; + return account_id.id != "managed_user@localhost"; } } // namespace @@ -109,7 +109,7 @@ bool AccountFetcherService::IsAllUserInfoFetched() const { } void AccountFetcherService::ForceRefreshOfAccountInfo( - const std::string& account_id) { + const CoreAccountId& account_id) { DCHECK(network_fetches_enabled_); RefreshAccountInfo(account_id, false); } @@ -137,24 +137,22 @@ void AccountFetcherService::EnableAccountRemovalForTest() { } void AccountFetcherService::RefreshAllAccountInfo(bool only_fetch_if_invalid) { - std::vector<std::string> accounts = token_service_->GetAccounts(); - for (std::vector<std::string>::const_iterator it = accounts.begin(); - it != accounts.end(); ++it) { - RefreshAccountInfo(*it, only_fetch_if_invalid); + for (const auto& account : token_service_->GetAccounts()) { + RefreshAccountInfo(account, only_fetch_if_invalid); } } // Child account status is refreshed through invalidations which are only // available for the primary account. Finding the primary account requires a -// dependency on signin_manager which we get around by only allowing a single -// account. This is possible since we only support a single account to be a -// child anyway. +// dependency on PrimaryAccountManager which we get around by only allowing a +// single account. This is possible since we only support a single account to be +// a child anyway. #if defined(OS_ANDROID) void AccountFetcherService::UpdateChildInfo() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::vector<std::string> accounts = token_service_->GetAccounts(); + std::vector<CoreAccountId> accounts = token_service_->GetAccounts(); if (accounts.size() == 1) { - const std::string& candidate = accounts[0]; + const CoreAccountId& candidate = accounts[0]; if (candidate == child_request_account_id_) return; if (!child_request_account_id_.empty()) @@ -208,7 +206,7 @@ void AccountFetcherService::ScheduleNextRefresh() { // Starts fetching user information. This is called periodically to refresh. void AccountFetcherService::StartFetchingUserInfo( - const std::string& account_id) { + const CoreAccountId& account_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(network_fetches_enabled_); @@ -228,7 +226,7 @@ void AccountFetcherService::StartFetchingUserInfo( #if defined(OS_ANDROID) // Starts fetching whether this is a child account. Handles refresh internally. void AccountFetcherService::StartFetchingChildInfo( - const std::string& account_id) { + const CoreAccountId& account_id) { child_info_request_ = ChildAccountInfoFetcherAndroid::Create(this, child_request_account_id_); } @@ -236,18 +234,18 @@ void AccountFetcherService::StartFetchingChildInfo( void AccountFetcherService::ResetChildInfo() { if (!child_request_account_id_.empty()) SetIsChildAccount(child_request_account_id_, false); - child_request_account_id_.clear(); + child_request_account_id_ = CoreAccountId(); child_info_request_.reset(); } -void AccountFetcherService::SetIsChildAccount(const std::string& account_id, +void AccountFetcherService::SetIsChildAccount(const CoreAccountId& account_id, bool is_child_account) { if (child_request_account_id_ == account_id) account_tracker_service_->SetIsChildAccount(account_id, is_child_account); } #endif -void AccountFetcherService::RefreshAccountInfo(const std::string& account_id, +void AccountFetcherService::RefreshAccountInfo(const CoreAccountId& account_id, bool only_fetch_if_invalid) { DCHECK(network_fetches_enabled_); account_tracker_service_->StartTrackingAccount(account_id); @@ -268,7 +266,7 @@ void AccountFetcherService::RefreshAccountInfo(const std::string& account_id, } void AccountFetcherService::OnUserInfoFetchSuccess( - const std::string& account_id, + const CoreAccountId& account_id, std::unique_ptr<base::DictionaryValue> user_info) { account_tracker_service_->SetAccountInfoFromUserInfo(account_id, user_info.get()); @@ -287,7 +285,7 @@ AccountFetcherService::GetOrCreateImageFetcher() { return image_fetcher_.get(); } -void AccountFetcherService::FetchAccountImage(const std::string& account_id) { +void AccountFetcherService::FetchAccountImage(const CoreAccountId& account_id) { DCHECK(signin_client_); std::string picture_url_string = account_tracker_service_->GetAccountInfo(account_id).picture_url; @@ -329,16 +327,16 @@ void AccountFetcherService::FetchAccountImage(const std::string& account_id) { } void AccountFetcherService::OnUserInfoFetchFailure( - const std::string& account_id) { + const CoreAccountId& account_id) { LOG(WARNING) << "Failed to get UserInfo for " << account_id; user_info_requests_.erase(account_id); } void AccountFetcherService::OnRefreshTokenAvailable( - const std::string& account_id) { + const CoreAccountId& account_id) { TRACE_EVENT1("AccountFetcherService", "AccountFetcherService::OnRefreshTokenAvailable", "account_id", - account_id); + account_id.id); DVLOG(1) << "AVAILABLE " << account_id; // The SigninClient needs a "final init" in order to perform some actions @@ -355,10 +353,10 @@ void AccountFetcherService::OnRefreshTokenAvailable( } void AccountFetcherService::OnRefreshTokenRevoked( - const std::string& account_id) { + const CoreAccountId& account_id) { TRACE_EVENT1("AccountFetcherService", "AccountFetcherService::OnRefreshTokenRevoked", "account_id", - account_id); + account_id.id); DVLOG(1) << "REVOKED " << account_id; // Short-circuit out if network fetches are not enabled. @@ -383,8 +381,8 @@ void AccountFetcherService::OnRefreshTokensLoaded() { } void AccountFetcherService::OnImageFetched( - const std::string& id, + const CoreAccountId& account_id, const gfx::Image& image, const image_fetcher::RequestMetadata&) { - account_tracker_service_->SetAccountImage(id, image); + account_tracker_service_->SetAccountImage(account_id, image); } diff --git a/chromium/components/signin/core/browser/account_fetcher_service.h b/chromium/components/signin/internal/identity_manager/account_fetcher_service.h index 0d94fba7ff6..0cd1249ff97 100644 --- a/chromium/components/signin/core/browser/account_fetcher_service.h +++ b/chromium/components/signin/internal/identity_manager/account_fetcher_service.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_CORE_BROWSER_ACCOUNT_FETCHER_SERVICE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_FETCHER_SERVICE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_FETCHER_SERVICE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_FETCHER_SERVICE_H_ #include <stdint.h> @@ -15,9 +15,7 @@ #include "base/sequence_checker.h" #include "base/timer/timer.h" #include "build/build_config.h" -#include "components/keyed_service/core/keyed_service.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "ui/gfx/image/image.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" class AccountInfoFetcher; class AccountTrackerService; @@ -33,13 +31,17 @@ namespace base { class DictionaryValue; } +namespace gfx { +class Image; +} + namespace image_fetcher { struct RequestMetadata; class ImageDecoder; class ImageFetcherImpl; } // namespace image_fetcher -class AccountFetcherService : public OAuth2TokenService::Observer { +class AccountFetcherService : public OAuth2TokenServiceObserver { public: // Name of the preference that tracks the int64_t representation of the last // time the AccountTrackerService was updated. @@ -65,7 +67,7 @@ class AccountFetcherService : public OAuth2TokenService::Observer { // there are still unfininshed fetchers. virtual bool IsAllUserInfoFetched() const; - void ForceRefreshOfAccountInfo(const std::string& account_id); + void ForceRefreshOfAccountInfo(const CoreAccountId& account_id); AccountTrackerService* account_tracker_service() const { return account_tracker_service_; @@ -88,12 +90,13 @@ class AccountFetcherService : public OAuth2TokenService::Observer { #if defined(OS_ANDROID) // Called by ChildAccountInfoFetcherAndroid. - void SetIsChildAccount(const std::string& account_id, bool is_child_account); + void SetIsChildAccount(const CoreAccountId& account_id, + bool is_child_account); #endif - // OAuth2TokenService::Observer implementation. - void OnRefreshTokenAvailable(const std::string& account_id) override; - void OnRefreshTokenRevoked(const std::string& account_id) override; + // OAuth2TokenServiceObserver implementation. + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override; + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override; void OnRefreshTokensLoaded() override; private: @@ -114,9 +117,9 @@ class AccountFetcherService : public OAuth2TokenService::Observer { // Virtual so that tests can override the network fetching behaviour. // Further the two fetches are managed by a different refresh logic and // thus, can not be combined. - void StartFetchingUserInfo(const std::string& account_id); + void StartFetchingUserInfo(const CoreAccountId& account_id); #if defined(OS_ANDROID) - void StartFetchingChildInfo(const std::string& account_id); + void StartFetchingChildInfo(const CoreAccountId& account_id); // If there is more than one account in a profile, we forcibly reset the // child status for an account to be false. @@ -124,20 +127,20 @@ class AccountFetcherService : public OAuth2TokenService::Observer { #endif // Refreshes the AccountInfo associated with |account_id|. - void RefreshAccountInfo(const std::string& account_id, + void RefreshAccountInfo(const CoreAccountId& account_id, bool only_fetch_if_invalid); // Called by AccountInfoFetcher. - void OnUserInfoFetchSuccess(const std::string& account_id, + void OnUserInfoFetchSuccess(const CoreAccountId& account_id, std::unique_ptr<base::DictionaryValue> user_info); - void OnUserInfoFetchFailure(const std::string& account_id); + void OnUserInfoFetchFailure(const CoreAccountId& account_id); image_fetcher::ImageFetcherImpl* GetOrCreateImageFetcher(); // Called in |OnUserInfoFetchSuccess| after the account info has been fetched. - void FetchAccountImage(const std::string& account_id); + void FetchAccountImage(const CoreAccountId& account_id); - void OnImageFetched(const std::string& id, + void OnImageFetched(const CoreAccountId& account_id, const gfx::Image& image, const image_fetcher::RequestMetadata& image_metadata); @@ -153,12 +156,12 @@ class AccountFetcherService : public OAuth2TokenService::Observer { base::OneShotTimer timer_; #if defined(OS_ANDROID) - std::string child_request_account_id_; + CoreAccountId child_request_account_id_; std::unique_ptr<ChildAccountInfoFetcherAndroid> child_info_request_; #endif // Holds references to account info fetchers keyed by account_id. - std::unordered_map<std::string, std::unique_ptr<AccountInfoFetcher>> + std::unordered_map<CoreAccountId, std::unique_ptr<AccountInfoFetcher>> user_info_requests_; // Used for fetching the account images. @@ -170,4 +173,4 @@ class AccountFetcherService : public OAuth2TokenService::Observer { DISALLOW_COPY_AND_ASSIGN(AccountFetcherService); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_FETCHER_SERVICE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_FETCHER_SERVICE_H_ diff --git a/chromium/components/signin/core/browser/account_info_fetcher.cc b/chromium/components/signin/internal/identity_manager/account_info_fetcher.cc index fee88e9e7b4..eedc41fb62e 100644 --- a/chromium/components/signin/core/browser/account_info_fetcher.cc +++ b/chromium/components/signin/internal/identity_manager/account_info_fetcher.cc @@ -2,27 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/core/browser/account_info_fetcher.h" +#include "components/signin/internal/identity_manager/account_info_fetcher.h" #include <utility> #include "base/trace_event/trace_event.h" -#include "components/signin/core/browser/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "google_apis/gaia/gaia_constants.h" #include "services/network/public/cpp/shared_url_loader_factory.h" AccountInfoFetcher::AccountInfoFetcher( - OAuth2TokenService* token_service, + ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, AccountFetcherService* service, - const std::string& account_id) - : OAuth2TokenService::Consumer("gaia_account_tracker"), + const CoreAccountId& account_id) + : OAuth2AccessTokenManager::Consumer("gaia_account_tracker"), token_service_(token_service), url_loader_factory_(std::move(url_loader_factory)), service_(service), account_id_(account_id) { TRACE_EVENT_ASYNC_BEGIN1("AccountFetcherService", "AccountIdFetcher", this, - "account_id", account_id); + "account_id", account_id.id); } AccountInfoFetcher::~AccountInfoFetcher() { @@ -30,7 +31,7 @@ AccountInfoFetcher::~AccountInfoFetcher() { } void AccountInfoFetcher::Start() { - OAuth2TokenService::ScopeSet scopes; + OAuth2AccessTokenManager::ScopeSet scopes; scopes.insert(GaiaConstants::kGoogleUserInfoEmail); scopes.insert(GaiaConstants::kGoogleUserInfoProfile); login_token_request_ = @@ -38,7 +39,7 @@ void AccountInfoFetcher::Start() { } void AccountInfoFetcher::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const OAuth2AccessTokenConsumer::TokenResponse& token_response) { TRACE_EVENT_ASYNC_STEP_PAST0("AccountFetcherService", "AccountIdFetcher", this, "OnGetTokenSuccess"); @@ -51,7 +52,7 @@ void AccountInfoFetcher::OnGetTokenSuccess( } void AccountInfoFetcher::OnGetTokenFailure( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) { TRACE_EVENT_ASYNC_STEP_PAST1("AccountFetcherService", "AccountIdFetcher", this, "OnGetTokenFailure", @@ -65,7 +66,7 @@ void AccountInfoFetcher::OnGetUserInfoResponse( std::unique_ptr<base::DictionaryValue> user_info) { TRACE_EVENT_ASYNC_STEP_PAST1("AccountFetcherService", "AccountIdFetcher", this, "OnGetUserInfoResponse", "account_id", - account_id_); + account_id_.id); service_->OnUserInfoFetchSuccess(account_id_, std::move(user_info)); } diff --git a/chromium/components/signin/core/browser/account_info_fetcher.h b/chromium/components/signin/internal/identity_manager/account_info_fetcher.h index 27cec56b3eb..c43ba6dbfb2 100644 --- a/chromium/components/signin/core/browser/account_info_fetcher.h +++ b/chromium/components/signin/internal/identity_manager/account_info_fetcher.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_CORE_BROWSER_ACCOUNT_INFO_FETCHER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_FETCHER_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_FETCHER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_FETCHER_H_ #include <memory> @@ -11,37 +11,38 @@ #include "base/memory/scoped_refptr.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_oauth_client.h" -#include "google_apis/gaia/oauth2_token_service.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" namespace network { class SharedURLLoaderFactory; } class AccountFetcherService; +class ProfileOAuth2TokenService; // An account information fetcher that gets an OAuth token of appropriate -// scope and uses it to fetch account infromation. This does not handle +// scope and uses it to fetch account information. This does not handle // refreshing the information and is meant to be used in a one shot fashion. -class AccountInfoFetcher : public OAuth2TokenService::Consumer, +class AccountInfoFetcher : public OAuth2AccessTokenManager::Consumer, public gaia::GaiaOAuthClient::Delegate { public: AccountInfoFetcher( - OAuth2TokenService* token_service, + ProfileOAuth2TokenService* token_service, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, AccountFetcherService* service, - const std::string& account_id); + const CoreAccountId& account_id); ~AccountInfoFetcher() override; - const std::string& account_id() { return account_id_; } + const CoreAccountId& account_id() { return account_id_; } // Start fetching the account information. void Start(); - // OAuth2TokenService::Consumer implementation. + // OAuth2AccessTokenManager::Consumer implementation. void OnGetTokenSuccess( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, + void OnGetTokenFailure(const OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) override; // gaia::GaiaOAuthClient::Delegate implementation. @@ -51,15 +52,15 @@ class AccountInfoFetcher : public OAuth2TokenService::Consumer, void OnNetworkError(int response_code) override; private: - OAuth2TokenService* token_service_; + ProfileOAuth2TokenService* token_service_; scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; AccountFetcherService* service_; - const std::string account_id_; + const CoreAccountId account_id_; - std::unique_ptr<OAuth2TokenService::Request> login_token_request_; + std::unique_ptr<OAuth2AccessTokenManager::Request> login_token_request_; std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; DISALLOW_COPY_AND_ASSIGN(AccountInfoFetcher); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_FETCHER_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/account_info_util.cc b/chromium/components/signin/internal/identity_manager/account_info_util.cc index 878adeb5f62..cb77753ba13 100644 --- a/chromium/components/signin/core/browser/account_info_util.cc +++ b/chromium/components/signin/internal/identity_manager/account_info_util.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/core/browser/account_info_util.h" +#include "components/signin/internal/identity_manager/account_info_util.h" -#include "components/signin/core/browser/account_info.h" +#include "components/signin/public/identity_manager/account_info.h" namespace { // Keys used to store the different values in the JSON dictionary received diff --git a/chromium/components/signin/core/browser/account_info_util.h b/chromium/components/signin/internal/identity_manager/account_info_util.h index fb2058a6674..53207e5c483 100644 --- a/chromium/components/signin/core/browser/account_info_util.h +++ b/chromium/components/signin/internal/identity_manager/account_info_util.h @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_UTIL_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_UTIL_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ #include "base/optional.h" #include "base/values.h" -#include "components/signin/core/browser/account_info.h" +#include "components/signin/public/identity_manager/account_info.h" // Builds an AccountInfo from the JSON data returned by the gaia servers (the // data should have been converted to base::Value), if possible. base::Optional<AccountInfo> AccountInfoFromUserInfo( const base::Value& user_info); -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_UTIL_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ diff --git a/chromium/components/signin/core/browser/account_info_util_unittest.cc b/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc index da0c76f172f..cc658a06104 100644 --- a/chromium/components/signin/core/browser/account_info_util_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/core/browser/account_info_util.h" +#include "components/signin/internal/identity_manager/account_info_util.h" -#include "components/signin/core/browser/account_info.h" +#include "components/signin/public/identity_manager/account_info.h" #include "testing/platform_test.h" namespace { diff --git a/chromium/components/signin/core/browser/account_tracker_service.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc index c30ba074019..710345b7cf6 100644 --- a/chromium/components/signin/core/browser/account_tracker_service.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.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/core/browser/account_tracker_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" #include <stddef.h> @@ -23,13 +23,13 @@ #include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" -#include "components/signin/core/browser/account_info_util.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/internal/identity_manager/account_info_util.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "ui/gfx/image/image.h" #if defined(OS_ANDROID) #include "base/android/jni_array.h" -#include "jni/AccountTrackerService_jni.h" +#include "components/signin/core/browser/android/jni_headers/AccountTrackerService_jni.h" #endif namespace { @@ -94,7 +94,7 @@ void RemoveImage(const base::FilePath& image_path) { } // namespace -AccountTrackerService::AccountTrackerService() : weak_factory_(this) { +AccountTrackerService::AccountTrackerService() { #if defined(OS_ANDROID) JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef<jobject> java_ref = @@ -136,14 +136,6 @@ void AccountTrackerService::Shutdown() { accounts_.clear(); } -void AccountTrackerService::AddObserver(Observer* observer) { - observer_list_.AddObserver(observer); -} - -void AccountTrackerService::RemoveObserver(Observer* observer) { - observer_list_.RemoveObserver(observer); -} - std::vector<AccountInfo> AccountTrackerService::GetAccounts() const { std::vector<AccountInfo> accounts; for (const auto& pair : accounts_) { @@ -209,20 +201,20 @@ void AccountTrackerService::SetMigrationDone() { void AccountTrackerService::NotifyAccountUpdated( const AccountInfo& account_info) { DCHECK(!account_info.gaia.empty()); - for (auto& observer : observer_list_) - observer.OnAccountUpdated(account_info); + if (on_account_updated_callback_) + on_account_updated_callback_.Run(account_info); } void AccountTrackerService::NotifyAccountRemoved( const AccountInfo& account_info) { DCHECK(!account_info.gaia.empty()); - for (auto& observer : observer_list_) - observer.OnAccountRemoved(account_info); + if (on_account_removed_callback_) + on_account_removed_callback_.Run(account_info); } void AccountTrackerService::StartTrackingAccount( const CoreAccountId& account_id) { - if (!base::ContainsKey(accounts_, account_id)) { + if (!base::Contains(accounts_, account_id)) { DVLOG(1) << "StartTracking " << account_id; AccountInfo account_info; account_info.account_id = account_id; @@ -234,7 +226,7 @@ void AccountTrackerService::StartTrackingAccount( void AccountTrackerService::StopTrackingAccount( const CoreAccountId& account_id) { DVLOG(1) << "StopTracking " << account_id; - if (base::ContainsKey(accounts_, account_id)) { + if (base::Contains(accounts_, account_id)) { AccountInfo account_info = std::move(accounts_[account_id]); RemoveFromPrefs(account_info); RemoveAccountImageFromDisk(account_id); @@ -248,7 +240,7 @@ void AccountTrackerService::StopTrackingAccount( void AccountTrackerService::SetAccountInfoFromUserInfo( const CoreAccountId& account_id, const base::DictionaryValue* user_info) { - DCHECK(base::ContainsKey(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; base::Optional<AccountInfo> maybe_account_info = @@ -271,7 +263,7 @@ void AccountTrackerService::SetAccountInfoFromUserInfo( void AccountTrackerService::SetAccountImage(const CoreAccountId& account_id, const gfx::Image& image) { - if (!base::ContainsKey(accounts_, account_id)) + if (!base::Contains(accounts_, account_id)) return; AccountInfo& account_info = accounts_[account_id]; account_info.account_image = image; @@ -281,7 +273,7 @@ void AccountTrackerService::SetAccountImage(const CoreAccountId& account_id, void AccountTrackerService::SetIsChildAccount(const CoreAccountId& account_id, bool is_child_account) { - DCHECK(base::ContainsKey(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; if (account_info.is_child_account == is_child_account) return; @@ -294,7 +286,7 @@ void AccountTrackerService::SetIsChildAccount(const CoreAccountId& account_id, void AccountTrackerService::SetIsAdvancedProtectionAccount( const CoreAccountId& account_id, bool is_under_advanced_protection) { - DCHECK(base::ContainsKey(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; if (account_info.is_under_advanced_protection == is_under_advanced_protection) return; @@ -304,6 +296,18 @@ void AccountTrackerService::SetIsAdvancedProtectionAccount( SaveToPrefs(account_info); } +void AccountTrackerService::SetOnAccountUpdatedCallback( + AccountInfoCallback callback) { + DCHECK(!on_account_updated_callback_); + on_account_updated_callback_ = callback; +} + +void AccountTrackerService::SetOnAccountRemovedCallback( + AccountInfoCallback callback) { + DCHECK(!on_account_removed_callback_); + on_account_removed_callback_ = callback; +} + void AccountTrackerService::MigrateToGaiaId() { DCHECK_EQ(GetMigrationState(), MIGRATION_IN_PROGRESS); @@ -319,7 +323,7 @@ void AccountTrackerService::MigrateToGaiaId() { // If there is already an account keyed to the current account's gaia id, // assume this is the result of a partial migration and skip the account // that is currently inspected. - if (base::ContainsKey(accounts_, new_account_id)) + if (base::Contains(accounts_, new_account_id)) continue; AccountInfo new_account_info = pair.second; @@ -340,7 +344,7 @@ void AccountTrackerService::MigrateToGaiaId() { // Remove any obsolete account. for (const auto& account_id : to_remove) { - DCHECK(base::ContainsKey(accounts_, account_id)); + DCHECK(base::Contains(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; RemoveAccountImageFromDisk(account_id); RemoveFromPrefs(account_info); @@ -353,7 +357,7 @@ bool AccountTrackerService::IsMigrationDone() const { return false; for (const auto& pair : accounts_) { - if (pair.first != pair.second.gaia) + if (pair.first.id != pair.second.gaia) return false; } @@ -374,7 +378,7 @@ AccountTrackerService::ComputeNewMigrationState() const { // Migration is required if at least one account is not keyed to its // gaia id. - migration_required |= (pair.first != pair.second.gaia); + migration_required |= (pair.first.id != pair.second.gaia); } return migration_required ? MIGRATION_IN_PROGRESS : MIGRATION_DONE; @@ -402,7 +406,7 @@ base::FilePath AccountTrackerService::GetImagePathFor( void AccountTrackerService::OnAccountImageLoaded( const CoreAccountId& account_id, gfx::Image image) { - if (base::ContainsKey(accounts_, account_id) && + if (base::Contains(accounts_, account_id) && accounts_[account_id].account_image.IsEmpty()) { AccountInfo& account_info = accounts_[account_id]; account_info.account_image = image; @@ -626,7 +630,7 @@ CoreAccountId AccountTrackerService::SeedAccountInfo(const std::string& gaia, CoreAccountId AccountTrackerService::SeedAccountInfo(AccountInfo info) { info.account_id = PickAccountIdForAccount(info.gaia, info.email); - const bool already_exists = base::ContainsKey(accounts_, info.account_id); + const bool already_exists = base::Contains(accounts_, info.account_id); StartTrackingAccount(info.account_id); AccountInfo& account_info = accounts_[info.account_id]; DCHECK(!already_exists || account_info.gaia.empty() || diff --git a/chromium/components/signin/core/browser/account_tracker_service.h b/chromium/components/signin/internal/identity_manager/account_tracker_service.h index e0511f891da..5fc76f4f017 100644 --- a/chromium/components/signin/core/browser/account_tracker_service.h +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_TRACKER_SERVICE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_TRACKER_SERVICE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_TRACKER_SERVICE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_TRACKER_SERVICE_H_ #include <list> #include <map> #include <string> #include <vector> +#include "base/callback.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -17,11 +18,9 @@ #include "base/sequence_checker.h" #include "base/timer/timer.h" #include "build/build_config.h" -#include "components/keyed_service/core/keyed_service.h" -#include "components/signin/core/browser/account_info.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" -#include "ui/gfx/image/image.h" #if defined(OS_ANDROID) #include "base/android/scoped_java_ref.h" @@ -34,7 +33,11 @@ namespace base { class DictionaryValue; } -namespace identity { +namespace gfx { +class Image; +} + +namespace signin { class IdentityManager; void SimulateSuccessfulFetchOfAccountInfo(IdentityManager*, const std::string&, @@ -45,19 +48,13 @@ void SimulateSuccessfulFetchOfAccountInfo(IdentityManager*, const std::string&, const std::string&, const std::string&); -} +} // namespace signin // Retrieves and caches GAIA information about Google Accounts. class AccountTrackerService { public: - // Clients of AccountTrackerService can implement this interface and register - // with AddObserver() to learn about account information changes. - class Observer { - public: - virtual ~Observer() {} - virtual void OnAccountUpdated(const AccountInfo& info) {} - virtual void OnAccountRemoved(const AccountInfo& info) {} - }; + typedef base::RepeatingCallback<void(const AccountInfo& info)> + AccountInfoCallback; // Possible values for the kAccountIdMigrationState preference. // Keep in sync with OAuth2LoginAccountRevokedMigrationState histogram enum. @@ -78,9 +75,6 @@ class AccountTrackerService { void Shutdown(); - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - // Initializes the list of accounts from |pref_service| and load images from // |user_data_dir|. If |user_data_dir| is empty, images will not be saved to // nor loaded from disk. @@ -134,6 +128,14 @@ class AccountTrackerService { base::android::ScopedJavaLocalRef<jobject> GetJavaObject(); #endif + // If set, this callback will be invoked whenever the details of a tracked + // account changes (e.g. account's info, image, |is_child_account|...). + void SetOnAccountUpdatedCallback(AccountInfoCallback callback); + + // If set, this callback will be invoked whenever an existing account with a + // valid GaiaId gets removed from |accounts_| (i.e. stops being tracked). + void SetOnAccountRemovedCallback(AccountInfoCallback callback); + protected: // Available to be called in tests. void SetAccountInfoFromUserInfo(const CoreAccountId& account_id, @@ -146,8 +148,8 @@ class AccountTrackerService { private: friend class AccountFetcherService; - friend void identity::SimulateSuccessfulFetchOfAccountInfo( - identity::IdentityManager*, + friend void signin::SimulateSuccessfulFetchOfAccountInfo( + signin::IdentityManager*, const std::string&, const std::string&, const std::string&, @@ -198,10 +200,11 @@ class AccountTrackerService { PrefService* pref_service_ = nullptr; // Not owned. std::map<CoreAccountId, AccountInfo> accounts_; - base::ObserverList<Observer>::Unchecked observer_list_; - base::FilePath user_data_dir_; + AccountInfoCallback on_account_updated_callback_; + AccountInfoCallback on_account_removed_callback_; + // Task runner used for file operations on avatar images. scoped_refptr<base::SequencedTaskRunner> image_storage_task_runner_; @@ -214,9 +217,9 @@ class AccountTrackerService { // Used to pass weak pointers of |this| to tasks created by // |image_storage_task_runner_|. - base::WeakPtrFactory<AccountTrackerService> weak_factory_; + base::WeakPtrFactory<AccountTrackerService> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(AccountTrackerService); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_TRACKER_SERVICE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_TRACKER_SERVICE_H_ diff --git a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc index 46eac21d21b..a11e96f3681 100644 --- a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc @@ -15,17 +15,16 @@ #include "build/build_config.h" #include "components/image_fetcher/core/fake_image_decoder.h" #include "components/image_fetcher/core/image_data_fetcher.h" -#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/prefs/testing_pref_service.h" -#include "components/signin/core/browser/account_fetcher_service.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/avatar_icon_util.h" -#include "components/signin/core/browser/fake_profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" +#include "components/signin/public/base/avatar_icon_util.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/account_info.h" #include "google_apis/gaia/gaia_oauth_client.h" #include "google_apis/gaia/gaia_urls.h" #include "net/http/http_status_code.h" @@ -34,7 +33,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_ANDROID) -#include "components/signin/core/browser/child_account_info_fetcher_android.h" +#include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h" #endif namespace { @@ -147,19 +146,12 @@ class TrackingEvent { account_id_.c_str(), gaia_id_.c_str(), email_.c_str()); } - private: - friend bool CompareByUser(TrackingEvent a, TrackingEvent b); - TrackingEventType type_; std::string account_id_; std::string gaia_id_; std::string email_; }; -bool CompareByUser(TrackingEvent a, TrackingEvent b) { - return a.account_id_ < b.account_id_; -} - std::string Str(const std::vector<TrackingEvent>& events) { std::string str = "["; bool needs_comma = false; @@ -173,60 +165,6 @@ std::string Str(const std::vector<TrackingEvent>& events) { return str; } -class AccountTrackerObserver : public AccountTrackerService::Observer { - public: - AccountTrackerObserver() {} - ~AccountTrackerObserver() override {} - - void Clear(); - void SortEventsByUser(); - - testing::AssertionResult CheckEvents( - const std::vector<TrackingEvent>& events); - - private: - // AccountTrackerService::Observer implementation - void OnAccountUpdated(const AccountInfo& ids) override; - void OnAccountRemoved(const AccountInfo& ids) override; - - std::vector<TrackingEvent> events_; -}; - -void AccountTrackerObserver::OnAccountUpdated(const AccountInfo& ids) { - events_.push_back( - TrackingEvent(UPDATED, ids.account_id, ids.gaia, ids.email)); -} - -void AccountTrackerObserver::OnAccountRemoved(const AccountInfo& ids) { - events_.push_back( - TrackingEvent(REMOVED, ids.account_id, ids.gaia, ids.email)); -} - -void AccountTrackerObserver::Clear() { - events_.clear(); -} - -void AccountTrackerObserver::SortEventsByUser() { - std::stable_sort(events_.begin(), events_.end(), CompareByUser); -} - -testing::AssertionResult AccountTrackerObserver::CheckEvents( - const std::vector<TrackingEvent>& events) { - std::string maybe_newline; - if ((events.size() + events_.size()) > 2) - maybe_newline = "\n"; - - testing::AssertionResult result( - (events_ == events) - ? testing::AssertionSuccess() - : (testing::AssertionFailure() - << "Expected " << maybe_newline << Str(events) << ", " - << maybe_newline << "Got " << maybe_newline << Str(events_))); - - events_.clear(); - return result; -} - } // namespace class AccountTrackerServiceTest : public testing::Test { @@ -240,6 +178,7 @@ class AccountTrackerServiceTest : public testing::Test { AccountTrackerService::RegisterPrefs(pref_service_.registry()); AccountFetcherService::RegisterPrefs(pref_service_.registry()); + ProfileOAuth2TokenService::RegisterProfilePrefs(pref_service_.registry()); } ~AccountTrackerServiceTest() override {} @@ -247,7 +186,7 @@ class AccountTrackerServiceTest : public testing::Test { void SetUp() override { testing::Test::SetUp(); CreateAccountTracker(base::FilePath(), /*network_enabled=*/true); - observer_.Clear(); + fake_oauth2_token_service_.LoadCredentials(""); } void TearDown() override { @@ -300,6 +239,36 @@ class AccountTrackerServiceTest : public testing::Test { EXPECT_EQ(AccountKeyToLocale(account_key), info.locale); } + testing::AssertionResult CheckAccountTrackerEvents( + const std::vector<TrackingEvent>& events) { + std::string maybe_newline; + if ((events.size() + account_tracker_events_.size()) > 2) + maybe_newline = "\n"; + + testing::AssertionResult result( + (account_tracker_events_ == events) + ? testing::AssertionSuccess() + : (testing::AssertionFailure() + << "Expected " << maybe_newline << Str(events) << ", " + << maybe_newline << "Got " << maybe_newline + << Str(account_tracker_events_))); + + account_tracker_events_.clear(); + return result; + } + + void ClearAccountTrackerEvents() { account_tracker_events_.clear(); } + + void OnAccountUpdated(const AccountInfo& ids) { + account_tracker_events_.push_back( + TrackingEvent(UPDATED, ids.account_id, ids.gaia, ids.email)); + } + + void OnAccountRemoved(const AccountInfo& ids) { + account_tracker_events_.push_back( + TrackingEvent(REMOVED, ids.account_id, ids.gaia, ids.email)); + } + // Helpers to fake access token and user info fetching void IssueAccessToken(AccountKey account_key) { fake_oauth2_token_service_.IssueAllTokensForAccount( @@ -337,7 +306,6 @@ class AccountTrackerServiceTest : public testing::Test { } SigninClient* signin_client() { return &signin_client_; } PrefService* prefs() { return &pref_service_; } - AccountTrackerObserver* observer() { return &observer_; } network::TestURLLoaderFactory* GetTestURLLoaderFactory() { return signin_client_.GetTestURLLoaderFactory(); @@ -364,10 +332,12 @@ class AccountTrackerServiceTest : public testing::Test { account_tracker_ = std::make_unique<AccountTrackerService>(); account_fetcher_ = std::make_unique<AccountFetcherService>(); - // Register observer before initialisation to allow the tests to check the - // events that are triggered during the initialisation. If a test is not - // interested in them, it can clear the observer before using it. - account_tracker_->AddObserver(&observer_); + // Register callbacks before initialisation to allow the tests to check the + // events that are triggered during the initialisation. + account_tracker_->SetOnAccountUpdatedCallback(base::BindRepeating( + &AccountTrackerServiceTest::OnAccountUpdated, base::Unretained(this))); + account_tracker_->SetOnAccountRemovedCallback(base::BindRepeating( + &AccountTrackerServiceTest::OnAccountRemoved, base::Unretained(this))); account_tracker_->Initialize(&pref_service_, std::move(path)); account_fetcher_->Initialize( @@ -385,9 +355,6 @@ class AccountTrackerServiceTest : public testing::Test { } if (account_tracker_) { - account_tracker_->RemoveObserver(&observer_); - observer_.Clear(); - account_tracker_->Shutdown(); account_tracker_.reset(); } @@ -395,10 +362,10 @@ class AccountTrackerServiceTest : public testing::Test { TestingPrefServiceSimple pref_service_; TestSigninClient signin_client_; - AccountTrackerObserver observer_; FakeProfileOAuth2TokenService fake_oauth2_token_service_; std::unique_ptr<AccountFetcherService> account_fetcher_; std::unique_ptr<AccountTrackerService> account_tracker_; + std::vector<TrackingEvent> account_tracker_events_; bool force_account_id_to_email_for_legacy_tests_ = false; }; @@ -456,21 +423,21 @@ TEST_F(AccountTrackerServiceTest, Basic) {} TEST_F(AccountTrackerServiceTest, TokenAvailable) { SimulateTokenAvailable(kAccountKeyAlpha); EXPECT_FALSE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TokenAvailable_Revoked) { SimulateTokenAvailable(kAccountKeyAlpha); SimulateTokenRevoked(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageSuccess) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -480,7 +447,7 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageSuccess) { ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) .account_image.IsEmpty()); ReturnAccountImageFetchSuccess(kAccountKeyAlpha); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -494,7 +461,7 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageFailure) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -513,13 +480,13 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_Revoked) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), })); SimulateTokenRevoked(kAccountKeyAlpha); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -530,14 +497,14 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfoFailed) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchFailure(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TokenAvailableTwice_UserInfoOnce) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -545,14 +512,14 @@ TEST_F(AccountTrackerServiceTest, TokenAvailableTwice_UserInfoOnce) { SimulateTokenAvailable(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TokenAlreadyExists) { SimulateTokenAvailable(kAccountKeyAlpha); EXPECT_FALSE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_TwoUserInfo) { @@ -561,7 +528,7 @@ TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_TwoUserInfo) { ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyBeta); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -576,14 +543,14 @@ TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_OneUserInfo) { SimulateTokenAvailable(kAccountKeyBeta); ReturnAccountInfoFetchSuccess(kAccountKeyBeta); EXPECT_FALSE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta), AccountKeyToGaiaId(kAccountKeyBeta), AccountKeyToEmail(kAccountKeyBeta)), })); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -703,9 +670,10 @@ TEST_F(AccountTrackerServiceTest, Persistence) { // Create a new tracker and make sure it loads the accounts (including the // images) correctly from persistence. + ClearAccountTrackerEvents(); ResetAccountTrackerWithPersistence(scoped_user_data_dir.GetPath()); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -715,7 +683,7 @@ TEST_F(AccountTrackerServiceTest, Persistence) { })); // Wait until all account images are loaded. scoped_task_environment_.RunUntilIdle(); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -776,7 +744,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfo) { EXPECT_EQ(account_id, infos[0].account_id); EXPECT_EQ(gaia_id, infos[0].gaia); EXPECT_EQ(email, infos[0].email); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, account_id, gaia_id, email), })); @@ -789,7 +757,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfo) { "remain the same"; EXPECT_EQ(gaia_id, infos[0].gaia); EXPECT_EQ(email_dotted, infos[0].email) << "Email should be changed"; - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, account_id, gaia_id, email_dotted), })); } @@ -807,7 +775,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { EXPECT_EQ(info.gaia, stored_info.gaia); EXPECT_EQ(info.email, stored_info.email); EXPECT_EQ(info.full_name, stored_info.full_name); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, info.account_id, info.gaia, info.email), })); @@ -822,7 +790,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { EXPECT_EQ(info.gaia, stored_info.gaia); EXPECT_EQ(info.email, stored_info.email); EXPECT_EQ(info.given_name, stored_info.given_name); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, info.account_id, info.gaia, info.email), })); @@ -833,7 +801,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { stored_info = account_tracker()->GetAccountInfo(info.account_id); EXPECT_EQ(info.gaia, stored_info.gaia); EXPECT_NE(info.given_name, stored_info.given_name); - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, UpgradeToFullAccountInfo) { @@ -861,9 +829,10 @@ TEST_F(AccountTrackerServiceTest, UpgradeToFullAccountInfo) { // Reinstantiate a tracker to validate that the AccountInfo saved to prefs // is now the upgraded one, considered valid. + ClearAccountTrackerEvents(); ResetAccountTrackerNetworkDisabled(); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyIncomplete), AccountKeyToGaiaId(kAccountKeyIncomplete), AccountKeyToEmail(kAccountKeyIncomplete)), @@ -877,7 +846,7 @@ TEST_F(AccountTrackerServiceTest, UpgradeToFullAccountInfo) { ASSERT_EQ(1u, infos.size()); EXPECT_TRUE(infos[0].IsValid()); // Check that no network fetches were made. - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); } TEST_F(AccountTrackerServiceTest, TimerRefresh) { @@ -1134,7 +1103,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountBasic) { #endif // Response was processed but observer is not notified as the account // state is invalid. - EXPECT_TRUE(observer()->CheckEvents({})); + EXPECT_TRUE(CheckAccountTrackerEvents({})); AccountInfo info = account_tracker()->GetAccountInfo( AccountKeyToAccountId(kAccountKeyChild)); EXPECT_TRUE(info.is_child_account); @@ -1153,7 +1122,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevoked) { #endif ReturnFetchResults(net::HTTP_OK, GenerateValidTokenInfoResponse(kAccountKeyChild)); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1162,7 +1131,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevoked) { AccountKeyToAccountId(kAccountKeyChild)); EXPECT_FALSE(info.is_child_account); SimulateTokenRevoked(kAccountKeyChild); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1181,7 +1150,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevokedWithUpdate) { #endif ReturnFetchResults(net::HTTP_OK, GenerateValidTokenInfoResponse(kAccountKeyChild)); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1192,7 +1161,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevokedWithUpdate) { SimulateTokenRevoked(kAccountKeyChild); #if defined(OS_ANDROID) // On Android, is_child_account is set to false before removing it. - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1201,7 +1170,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevokedWithUpdate) { AccountKeyToEmail(kAccountKeyChild)), })); #else - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1222,7 +1191,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedTwiceThenRevoked) { account_tracker()->SetIsChildAccount(AccountKeyToAccountId(kAccountKeyChild), true); #endif - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1233,7 +1202,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedTwiceThenRevoked) { SimulateTokenRevoked(kAccountKeyChild); #if defined(OS_ANDROID) // On Android, is_child_account is set to false before removing it. - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1242,7 +1211,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedTwiceThenRevoked) { AccountKeyToEmail(kAccountKeyChild)), })); #else - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1267,7 +1236,7 @@ TEST_F(AccountTrackerServiceTest, ChildAccountGraduation) { EXPECT_TRUE(info.is_child_account); ReturnFetchResults(net::HTTP_OK, GenerateValidTokenInfoResponse(kAccountKeyChild)); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1284,14 +1253,14 @@ TEST_F(AccountTrackerServiceTest, ChildAccountGraduation) { info = account_tracker()->GetAccountInfo( AccountKeyToAccountId(kAccountKeyChild)); EXPECT_FALSE(info.is_child_account); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), })); SimulateTokenRevoked(kAccountKeyChild); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), AccountKeyToGaiaId(kAccountKeyChild), AccountKeyToEmail(kAccountKeyChild)), @@ -1302,7 +1271,7 @@ TEST_F(AccountTrackerServiceTest, RemoveAccountBeforeImageFetchDone) { SimulateTokenAvailable(kAccountKeyAlpha); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), @@ -1310,7 +1279,7 @@ TEST_F(AccountTrackerServiceTest, RemoveAccountBeforeImageFetchDone) { SimulateTokenRevoked(kAccountKeyAlpha); ReturnAccountImageFetchFailure(kAccountKeyAlpha); - EXPECT_TRUE(observer()->CheckEvents({ + EXPECT_TRUE(CheckAccountTrackerEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha), AccountKeyToGaiaId(kAccountKeyAlpha), AccountKeyToEmail(kAccountKeyAlpha)), 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 new file mode 100644 index 00000000000..d2b158e4d04 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc @@ -0,0 +1,66 @@ +// 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/internal/identity_manager/accounts_cookie_mutator_impl.h" + +#include <utility> + +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/google_service_auth_error.h" + +namespace signin { + +AccountsCookieMutatorImpl::AccountsCookieMutatorImpl( + GaiaCookieManagerService* gaia_cookie_manager_service, + AccountTrackerService* account_tracker_service) + : gaia_cookie_manager_service_(gaia_cookie_manager_service), + account_tracker_service_(account_tracker_service) { + DCHECK(gaia_cookie_manager_service_); + DCHECK(account_tracker_service_); +} + +AccountsCookieMutatorImpl::~AccountsCookieMutatorImpl() {} + +void AccountsCookieMutatorImpl::AddAccountToCookie( + const CoreAccountId& account_id, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) { + gaia_cookie_manager_service_->AddAccountToCookie( + account_id, source, std::move(completion_callback)); +} + +void AccountsCookieMutatorImpl::AddAccountToCookieWithToken( + const CoreAccountId& account_id, + const std::string& access_token, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) { + gaia_cookie_manager_service_->AddAccountToCookieWithToken( + account_id, access_token, source, std::move(completion_callback)); +} + +void AccountsCookieMutatorImpl::SetAccountsInCookie( + const std::vector<CoreAccountId>& account_ids, + gaia::GaiaSource source, + base::OnceCallback<void(SetAccountsInCookieResult)> + set_accounts_in_cookies_completed_callback) { + std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> accounts; + for (const auto& account_id : account_ids) { + accounts.push_back(make_pair( + account_id, account_tracker_service_->GetAccountInfo(account_id).gaia)); + } + gaia_cookie_manager_service_->SetAccountsInCookie( + accounts, source, std::move(set_accounts_in_cookies_completed_callback)); +} + +void AccountsCookieMutatorImpl::TriggerCookieJarUpdate() { + gaia_cookie_manager_service_->TriggerListAccounts(); +} + +void AccountsCookieMutatorImpl::LogOutAllAccounts(gaia::GaiaSource source) { + gaia_cookie_manager_service_->LogOutAllAccounts(source); +} + +} // 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 new file mode 100644 index 00000000000..a083bbb1915 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h @@ -0,0 +1,61 @@ +// 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_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" + +class AccountTrackerService; +class GaiaCookieManagerService; + +namespace gaia { +class GaiaSource; +} + +namespace signin { + +// Concrete implementation of the AccountsCookieMutator interface. +class AccountsCookieMutatorImpl : public AccountsCookieMutator { + public: + explicit AccountsCookieMutatorImpl( + GaiaCookieManagerService* gaia_cookie_manager_service, + AccountTrackerService* account_tracker_service); + ~AccountsCookieMutatorImpl() override; + + void AddAccountToCookie( + const CoreAccountId& account_id, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) override; + + void AddAccountToCookieWithToken( + const CoreAccountId& account_id, + const std::string& access_token, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) override; + + void SetAccountsInCookie( + const std::vector<CoreAccountId>& account_ids, + gaia::GaiaSource source, + base::OnceCallback<void(SetAccountsInCookieResult)> + set_accounts_in_cookies_completed_callback) override; + + void TriggerCookieJarUpdate() override; + + void LogOutAllAccounts(gaia::GaiaSource source) override; + + private: + GaiaCookieManagerService* gaia_cookie_manager_service_; + AccountTrackerService* account_tracker_service_; + + DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorImpl); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_ diff --git a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc new file mode 100644 index 00000000000..bff6a3de620 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc @@ -0,0 +1,116 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/internal/identity_manager/accounts_mutator_impl.h" + +#include "base/optional.h" +#include "components/prefs/pref_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/device_id_helper.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/gaia_constants.h" + +namespace signin { + +AccountsMutatorImpl::AccountsMutatorImpl( + ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + PrimaryAccountManager* primary_account_manager, + PrefService* pref_service) + : token_service_(token_service), + account_tracker_service_(account_tracker_service), + primary_account_manager_(primary_account_manager) { + DCHECK(token_service_); + DCHECK(account_tracker_service_); + DCHECK(primary_account_manager_); +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + pref_service_ = pref_service; + DCHECK(pref_service_); +#endif +} + +AccountsMutatorImpl::~AccountsMutatorImpl() {} + +CoreAccountId AccountsMutatorImpl::AddOrUpdateAccount( + const std::string& gaia_id, + const std::string& email, + const std::string& refresh_token, + bool is_under_advanced_protection, + signin_metrics::SourceForRefreshTokenOperation source) { + CoreAccountId account_id = + account_tracker_service_->SeedAccountInfo(gaia_id, email); + account_tracker_service_->SetIsAdvancedProtectionAccount( + account_id, is_under_advanced_protection); + token_service_->UpdateCredentials(account_id, refresh_token, source); + + return account_id; +} + +void AccountsMutatorImpl::UpdateAccountInfo( + const CoreAccountId& account_id, + base::Optional<bool> is_child_account, + base::Optional<bool> is_under_advanced_protection) { + if (is_child_account.has_value()) { + account_tracker_service_->SetIsChildAccount(account_id, + is_child_account.value()); + } + + if (is_under_advanced_protection.has_value()) { + account_tracker_service_->SetIsAdvancedProtectionAccount( + account_id, is_under_advanced_protection.value()); + } +} + +void AccountsMutatorImpl::RemoveAccount( + const CoreAccountId& account_id, + signin_metrics::SourceForRefreshTokenOperation source) { + token_service_->RevokeCredentials(account_id, source); +} + +void AccountsMutatorImpl::RemoveAllAccounts( + signin_metrics::SourceForRefreshTokenOperation source) { + token_service_->RevokeAllCredentials(source); +} + +void AccountsMutatorImpl::InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation source) { + DCHECK(primary_account_manager_->IsAuthenticated()); + AccountInfo primary_account_info = + primary_account_manager_->GetAuthenticatedAccountInfo(); + AddOrUpdateAccount(primary_account_info.gaia, primary_account_info.email, + GaiaConstants::kInvalidRefreshToken, + primary_account_info.is_under_advanced_protection, source); +} + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +void AccountsMutatorImpl::MoveAccount(AccountsMutator* target, + const CoreAccountId& account_id) { + AccountInfo account_info = + account_tracker_service_->GetAccountInfo(account_id); + DCHECK(!account_info.account_id.empty()); + + auto* target_impl = static_cast<AccountsMutatorImpl*>(target); + target_impl->account_tracker_service_->SeedAccountInfo(account_info); + token_service_->ExtractCredentials(target_impl->token_service_, account_id); + + // Reset the device ID from the source mutator: the exported token is linked + // to the device ID of the current mutator on the server. Reset the device ID + // of the current mutator to avoid tying it with the new mutator. See + // https://crbug.com/813928#c16 + RecreateSigninScopedDeviceId(pref_service_); +} +#endif + +void AccountsMutatorImpl::LegacySetRefreshTokenForSupervisedUser( + const std::string& refresh_token) { + token_service_->UpdateCredentials( + CoreAccountId("managed_user@localhost"), refresh_token, + signin_metrics::SourceForRefreshTokenOperation::kSupervisedUser_InitSync); +} + +} // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h new file mode 100644 index 00000000000..0dc2ca05dc2 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_IMPL_H_ + +#include <string> + +#include "base/macros.h" +#include "components/signin/public/base/signin_buildflags.h" +#include "components/signin/public/identity_manager/accounts_mutator.h" + +namespace signin_metrics { +enum class SourceForRefreshTokenOperation; +} + +class AccountTrackerService; +struct CoreAccountId; +class PrefService; +class PrimaryAccountManager; +class ProfileOAuth2TokenService; + +namespace signin { + +// Concrete implementation of the AccountsMutatorImpl interface. +class AccountsMutatorImpl : public AccountsMutator { + public: + explicit AccountsMutatorImpl(ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + PrimaryAccountManager* primary_account_manager, + PrefService* pref_service); + ~AccountsMutatorImpl() override; + + // AccountsMutator: + CoreAccountId AddOrUpdateAccount( + const std::string& gaia_id, + const std::string& email, + const std::string& refresh_token, + bool is_under_advanced_protection, + signin_metrics::SourceForRefreshTokenOperation source) override; + void UpdateAccountInfo( + const CoreAccountId& account_id, + base::Optional<bool> is_child_account, + base::Optional<bool> is_under_advanced_protection) override; + void RemoveAccount( + const CoreAccountId& account_id, + signin_metrics::SourceForRefreshTokenOperation source) override; + void RemoveAllAccounts( + signin_metrics::SourceForRefreshTokenOperation source) override; + void InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation source) override; + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + void MoveAccount(AccountsMutator* target, + const CoreAccountId& account_id) override; +#endif + + void LegacySetRefreshTokenForSupervisedUser( + const std::string& refresh_token) override; + + private: + ProfileOAuth2TokenService* token_service_; + AccountTrackerService* account_tracker_service_; + PrimaryAccountManager* primary_account_manager_; +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + PrefService* pref_service_; +#endif + + DISALLOW_COPY_AND_ASSIGN(AccountsMutatorImpl); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_IMPL_H_ diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc index 85c6304d81b..77f1d593643 100644 --- a/chromium/components/signin/core/browser/child_account_info_fetcher_android.cc +++ b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc @@ -2,23 +2,23 @@ // 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/child_account_info_fetcher_android.h" +#include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h" #include <memory> #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/memory/ptr_util.h" -#include "components/signin/core/browser/account_fetcher_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "jni/ChildAccountInfoFetcher_jni.h" +#include "components/signin/core/browser/android/jni_headers/ChildAccountInfoFetcher_jni.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" using base::android::JavaParamRef; // static std::unique_ptr<ChildAccountInfoFetcherAndroid> ChildAccountInfoFetcherAndroid::Create(AccountFetcherService* service, - const std::string& account_id) { + const CoreAccountId& account_id) { std::string account_name = service->account_tracker_service()->GetAccountInfo(account_id).email; // The AccountTrackerService may not be populated correctly in tests. @@ -38,12 +38,12 @@ void ChildAccountInfoFetcherAndroid::InitializeForTests() { ChildAccountInfoFetcherAndroid::ChildAccountInfoFetcherAndroid( AccountFetcherService* service, - const std::string& account_id, + const CoreAccountId& account_id, const std::string& account_name) { JNIEnv* env = base::android::AttachCurrentThread(); j_child_account_info_fetcher_.Reset(Java_ChildAccountInfoFetcher_create( env, reinterpret_cast<jlong>(service), - base::android::ConvertUTF8ToJavaString(env, account_id), + base::android::ConvertUTF8ToJavaString(env, account_id.id), base::android::ConvertUTF8ToJavaString(env, account_name))); } @@ -60,6 +60,6 @@ void JNI_ChildAccountInfoFetcher_SetIsChildAccount( AccountFetcherService* service = reinterpret_cast<AccountFetcherService*>(native_service); service->SetIsChildAccount( - base::android::ConvertJavaStringToUTF8(env, j_account_id), + CoreAccountId(base::android::ConvertJavaStringToUTF8(env, j_account_id)), is_child_account); } diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h index 1007fe59fd3..2b08d1229fb 100644 --- a/chromium/components/signin/core/browser/child_account_info_fetcher_android.h +++ b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h @@ -2,13 +2,14 @@ // 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_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ #include <jni.h> #include <string> #include "base/android/scoped_java_ref.h" +#include "google_apis/gaia/core_account_id.h" class AccountFetcherService; @@ -16,14 +17,14 @@ class ChildAccountInfoFetcherAndroid { public: static std::unique_ptr<ChildAccountInfoFetcherAndroid> Create( AccountFetcherService* service, - const std::string& account_id); + const CoreAccountId& account_id); ~ChildAccountInfoFetcherAndroid(); static void InitializeForTests(); private: ChildAccountInfoFetcherAndroid(AccountFetcherService* service, - const std::string& account_id, + const CoreAccountId& account_id, const std::string& account_name); base::android::ScopedJavaGlobalRef<jobject> j_child_account_info_fetcher_; @@ -31,4 +32,4 @@ class ChildAccountInfoFetcherAndroid { DISALLOW_COPY_AND_ASSIGN(ChildAccountInfoFetcherAndroid); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ diff --git a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc new file mode 100644 index 00000000000..d2a4f923394 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc @@ -0,0 +1,29 @@ +// 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/internal/identity_manager/device_accounts_synchronizer_impl.h" + +#include "base/logging.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" + +namespace signin { + +DeviceAccountsSynchronizerImpl::DeviceAccountsSynchronizerImpl( + ProfileOAuth2TokenServiceDelegate* token_service_delegate) + : token_service_delegate_(token_service_delegate) { + DCHECK(token_service_delegate_); +} + +DeviceAccountsSynchronizerImpl::~DeviceAccountsSynchronizerImpl() = default; + +void DeviceAccountsSynchronizerImpl::ReloadAllAccountsFromSystem() { + token_service_delegate_->ReloadAllAccountsFromSystem(); +} + +void DeviceAccountsSynchronizerImpl::ReloadAccountFromSystem( + const CoreAccountId& account_id) { + token_service_delegate_->ReloadAccountFromSystem(account_id); +} + +} // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h new file mode 100644 index 00000000000..74679a0a9a1 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h @@ -0,0 +1,31 @@ +// 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_INTERNAL_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_IMPL_H_ + +#include "components/signin/public/identity_manager/device_accounts_synchronizer.h" + +class ProfileOAuth2TokenServiceDelegate; + +namespace signin { + +// Concrete implementation of DeviceAccountsSynchronizer interface. +class DeviceAccountsSynchronizerImpl : public DeviceAccountsSynchronizer { + public: + explicit DeviceAccountsSynchronizerImpl( + ProfileOAuth2TokenServiceDelegate* token_service_delegate); + ~DeviceAccountsSynchronizerImpl() override; + + // DeviceAccountsSynchronizer implementation. + void ReloadAllAccountsFromSystem() override; + void ReloadAccountFromSystem(const CoreAccountId& account_id) override; + + private: + ProfileOAuth2TokenServiceDelegate* token_service_delegate_ = nullptr; +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_IMPL_H_ diff --git a/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.cc b/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.cc new file mode 100644 index 00000000000..b69036efb23 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.cc @@ -0,0 +1,44 @@ +// 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/internal/identity_manager/diagnostics_provider_impl.h" + +#include "components/signin/internal/identity_manager/gaia_cookie_manager_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" + +namespace signin { + +DiagnosticsProviderImpl::DiagnosticsProviderImpl( + ProfileOAuth2TokenService* profile_oauth2_token_service, + GaiaCookieManagerService* gaia_cookie_manager_service) + : gaia_cookie_manager_service_(gaia_cookie_manager_service), + profile_oauth2_token_service_(profile_oauth2_token_service) { + DCHECK(gaia_cookie_manager_service_); + DCHECK(profile_oauth2_token_service_); +} + +DiagnosticsProviderImpl::~DiagnosticsProviderImpl() {} + +signin::LoadCredentialsState +DiagnosticsProviderImpl::GetDetailedStateOfLoadingOfRefreshTokens() const { + DCHECK(profile_oauth2_token_service_->GetDelegate()); + return profile_oauth2_token_service_->GetDelegate()->load_credentials_state(); +} + +base::TimeDelta +DiagnosticsProviderImpl::GetDelayBeforeMakingAccessTokenRequests() const { + const net::BackoffEntry* backoff_entry = + profile_oauth2_token_service_->GetDelegateBackoffEntry(); + return backoff_entry ? backoff_entry->GetTimeUntilRelease() + : base::TimeDelta(); +} + +base::TimeDelta DiagnosticsProviderImpl::GetDelayBeforeMakingCookieRequests() + const { + DCHECK(gaia_cookie_manager_service_->GetBackoffEntry()); + return gaia_cookie_manager_service_->GetBackoffEntry()->GetTimeUntilRelease(); +} + +} // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.h b/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.h new file mode 100644 index 00000000000..2035715c708 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/diagnostics_provider_impl.h @@ -0,0 +1,45 @@ +// 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_INTERNAL_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_IMPL_H_ + +#include "base/macros.h" +#include "components/signin/public/identity_manager/diagnostics_provider.h" + +class GaiaCookieManagerService; +class ProfileOAuth2TokenService; + +namespace signin { + +// Concrete implementation of the DiagnosticsProvider interface. +class DiagnosticsProviderImpl final : public DiagnosticsProvider { + public: + DiagnosticsProviderImpl( + ProfileOAuth2TokenService* profile_oauth2_token_service, + GaiaCookieManagerService* gaia_cookie_manager_service); + ~DiagnosticsProviderImpl() override; + + // Returns the state of the load credentials operation. + signin::LoadCredentialsState GetDetailedStateOfLoadingOfRefreshTokens() + const override; + + // Returns the time until a access token request can be sent (will be zero if + // the release time is in the past). + base::TimeDelta GetDelayBeforeMakingAccessTokenRequests() const override; + + // Returns the time until a cookie request can be sent (will be zero if the + // release time is in the past). + base::TimeDelta GetDelayBeforeMakingCookieRequests() const override; + + private: + GaiaCookieManagerService* gaia_cookie_manager_service_; + ProfileOAuth2TokenService* profile_oauth2_token_service_; + + DISALLOW_COPY_AND_ASSIGN(DiagnosticsProviderImpl); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_IMPL_H_ diff --git a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service.cc b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service.cc new file mode 100644 index 00000000000..586ce74d74f --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service.cc @@ -0,0 +1,101 @@ +// Copyright 2013 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/internal/identity_manager/fake_profile_oauth2_token_service.h" + +#include <memory> + +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" + +FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService( + PrefService* user_prefs) + : FakeProfileOAuth2TokenService( + user_prefs, + std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()) {} + +FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService( + PrefService* user_prefs, + std::unique_ptr<ProfileOAuth2TokenServiceDelegate> delegate) + : ProfileOAuth2TokenService(user_prefs, std::move(delegate)) { + OverrideAccessTokenManagerForTesting( + std::make_unique<FakeOAuth2AccessTokenManager>( + this /* OAuth2AccessTokenManager::Delegate* */)); +} + +FakeProfileOAuth2TokenService::~FakeProfileOAuth2TokenService() {} + +void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( + const std::string& account_id, + const std::string& access_token, + const base::Time& expiration) { + GetFakeAccessTokenManager()->IssueAllTokensForAccount( + account_id, access_token, expiration); +} + +void FakeProfileOAuth2TokenService::IssueAllTokensForAccount( + const std::string& account_id, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { + GetFakeAccessTokenManager()->IssueAllTokensForAccount(account_id, + token_response); +} + +void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequestsForAccount( + const std::string& account_id, + const GoogleServiceAuthError& error) { + GetFakeAccessTokenManager()->IssueErrorForAllPendingRequestsForAccount( + account_id, error); +} + +void FakeProfileOAuth2TokenService::IssueTokenForScope( + const OAuth2AccessTokenManager::ScopeSet& scope, + const std::string& access_token, + const base::Time& expiration) { + GetFakeAccessTokenManager()->IssueTokenForScope(scope, access_token, + expiration); +} + +void FakeProfileOAuth2TokenService::IssueTokenForScope( + const OAuth2AccessTokenManager::ScopeSet& scope, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { + GetFakeAccessTokenManager()->IssueTokenForScope(scope, token_response); +} + +void FakeProfileOAuth2TokenService::IssueErrorForScope( + const OAuth2AccessTokenManager::ScopeSet& scope, + const GoogleServiceAuthError& error) { + GetFakeAccessTokenManager()->IssueErrorForScope(scope, error); +} + +void FakeProfileOAuth2TokenService::IssueErrorForAllPendingRequests( + const GoogleServiceAuthError& error) { + GetFakeAccessTokenManager()->IssueErrorForAllPendingRequests(error); +} + +void FakeProfileOAuth2TokenService:: + set_auto_post_fetch_response_on_message_loop(bool auto_post_response) { + GetFakeAccessTokenManager()->set_auto_post_fetch_response_on_message_loop( + auto_post_response); +} + +void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests( + const std::string& access_token, + const base::Time& expiration) { + GetFakeAccessTokenManager()->IssueTokenForAllPendingRequests(access_token, + expiration); +} + +void FakeProfileOAuth2TokenService::IssueTokenForAllPendingRequests( + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { + GetFakeAccessTokenManager()->IssueTokenForAllPendingRequests(token_response); +} + +std::vector<FakeOAuth2AccessTokenManager::PendingRequest> +FakeProfileOAuth2TokenService::GetPendingRequests() { + return GetFakeAccessTokenManager()->GetPendingRequests(); +} + +FakeOAuth2AccessTokenManager* +FakeProfileOAuth2TokenService::GetFakeAccessTokenManager() { + return static_cast<FakeOAuth2AccessTokenManager*>(GetAccessTokenManager()); +} diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h index be71e20267d..f06a83a1b14 100644 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h +++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h @@ -2,20 +2,16 @@ // 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_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ #include <string> #include <vector> #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" - -namespace network { -class SharedURLLoaderFactory; -} +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "google_apis/gaia/fake_oauth2_access_token_manager.h" // Helper class to simplify writing unittests that depend on an instance of // ProfileOAuth2TokenService. @@ -30,7 +26,7 @@ class SharedURLLoaderFactory; // EXPECT_GT(0U, service.GetPendingRequests().size()); // ... // // Make any pending token fetches for a given scope succeed. -// ScopeSet scopes; +// OAuth2AccessTokenManager::ScopeSet scopes; // scopes.insert(GaiaConstants::kYourServiceScope); // IssueTokenForScope(scopes, "access_token", base::Time()::Max()); // ... @@ -39,28 +35,16 @@ class SharedURLLoaderFactory; // class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService { public: - struct PendingRequest { - PendingRequest(); - PendingRequest(const PendingRequest& other); - ~PendingRequest(); - - std::string account_id; - std::string client_id; - std::string client_secret; - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory; - ScopeSet scopes; - base::WeakPtr<RequestImpl> request; - }; - explicit FakeProfileOAuth2TokenService(PrefService* user_prefs); FakeProfileOAuth2TokenService( PrefService* user_prefs, - std::unique_ptr<OAuth2TokenServiceDelegate> delegate); + std::unique_ptr<ProfileOAuth2TokenServiceDelegate> delegate); ~FakeProfileOAuth2TokenService() override; // Gets a list of active requests (can be used by tests to validate that the // correct request has been issued). - std::vector<PendingRequest> GetPendingRequests(); + std::vector<FakeOAuth2AccessTokenManager::PendingRequest> + GetPendingRequests(); // Helper routines to issue tokens for pending requests. void IssueAllTokensForAccount(const std::string& account_id, @@ -76,15 +60,15 @@ class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService { const std::string& account_id, const GoogleServiceAuthError& error); - void IssueTokenForScope(const ScopeSet& scopes, + void IssueTokenForScope(const OAuth2AccessTokenManager::ScopeSet& scopes, const std::string& access_token, const base::Time& expiration); void IssueTokenForScope( - const ScopeSet& scopes, + const OAuth2AccessTokenManager::ScopeSet& scopes, const OAuth2AccessTokenConsumer::TokenResponse& token_response); - void IssueErrorForScope(const ScopeSet& scopes, + void IssueErrorForScope(const OAuth2AccessTokenManager::ScopeSet& scopes, const GoogleServiceAuthError& error); void IssueTokenForAllPendingRequests(const std::string& access_token, @@ -95,52 +79,12 @@ class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService { void IssueErrorForAllPendingRequests(const GoogleServiceAuthError& error); - void set_auto_post_fetch_response_on_message_loop(bool auto_post_response) { - auto_post_fetch_response_on_message_loop_ = auto_post_response; - } - - protected: - // OAuth2TokenService overrides. - void CancelAllRequests() override; - - void CancelRequestsForAccount(const std::string& account_id) override; - - void FetchOAuth2Token( - RequestImpl* request, - const std::string& account_id, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, - const std::string& client_id, - const std::string& client_secret, - const ScopeSet& scopes) override; - - void InvalidateAccessTokenImpl(const std::string& account_id, - const std::string& client_id, - const ScopeSet& scopes, - const std::string& access_token) override; + void set_auto_post_fetch_response_on_message_loop(bool auto_post_response); private: - // Helper function to complete pending requests - if |all_scopes| is true, - // then all pending requests are completed, otherwise, only those requests - // matching |scopes| are completed. If |account_id| is empty, then pending - // requests for all accounts are completed, otherwise only requests for the - // given account. - void CompleteRequests( - const std::string& account_id, - bool all_scopes, - const ScopeSet& scopes, - const GoogleServiceAuthError& error, - const OAuth2AccessTokenConsumer::TokenResponse& token_response); - - std::vector<PendingRequest> pending_requests_; - - // If true, then this fake service will post responses to - // |FetchOAuth2Token| on the current run loop. There is no need to call - // |IssueTokenForScope| in this case. - bool auto_post_fetch_response_on_message_loop_; - - base::WeakPtrFactory<FakeProfileOAuth2TokenService> weak_ptr_factory_; + FakeOAuth2AccessTokenManager* GetFakeAccessTokenManager(); DISALLOW_COPY_AND_ASSIGN(FakeProfileOAuth2TokenService); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_H_ diff --git a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc new file mode 100644 index 00000000000..701bbe89c68 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.cc @@ -0,0 +1,170 @@ +// Copyright 2015 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/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" + +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h" + +namespace { +// Values used from |MutableProfileOAuth2TokenServiceDelegate|. +const net::BackoffEntry::Policy kBackoffPolicy = { + 0 /* int num_errors_to_ignore */, + + 1000 /* int initial_delay_ms */, + + 2.0 /* double multiply_factor */, + + 0.2 /* double jitter_factor */, + + 15 * 60 * 1000 /* int64_t maximum_backoff_ms */, + + -1 /* int64_t entry_lifetime_ms */, + + false /* bool always_use_initial_delay */, +}; +} // namespace + +FakeProfileOAuth2TokenServiceDelegate::AccountInfo::AccountInfo( + const std::string& refresh_token) + : refresh_token(refresh_token), error(GoogleServiceAuthError::NONE) {} + +FakeProfileOAuth2TokenServiceDelegate::FakeProfileOAuth2TokenServiceDelegate() + : shared_factory_( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory_)), + backoff_entry_(&kBackoffPolicy) {} + +FakeProfileOAuth2TokenServiceDelegate:: + ~FakeProfileOAuth2TokenServiceDelegate() = default; + +std::unique_ptr<OAuth2AccessTokenFetcher> +FakeProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) { + auto it = refresh_tokens_.find(account_id); + DCHECK(it != refresh_tokens_.end()); + return std::make_unique<OAuth2AccessTokenFetcherImpl>( + consumer, url_loader_factory, it->second->refresh_token); +} + +bool FakeProfileOAuth2TokenServiceDelegate::RefreshTokenIsAvailable( + const CoreAccountId& account_id) const { + return !GetRefreshToken(account_id).empty(); +} + +GoogleServiceAuthError FakeProfileOAuth2TokenServiceDelegate::GetAuthError( + const CoreAccountId& account_id) const { + auto it = refresh_tokens_.find(account_id); + return (it == refresh_tokens_.end()) ? GoogleServiceAuthError::AuthErrorNone() + : it->second->error; +} + +std::string FakeProfileOAuth2TokenServiceDelegate::GetRefreshToken( + const CoreAccountId& account_id) const { + auto it = refresh_tokens_.find(account_id); + if (it != refresh_tokens_.end()) + return it->second->refresh_token; + return std::string(); +} + +const net::BackoffEntry* FakeProfileOAuth2TokenServiceDelegate::BackoffEntry() + const { + return &backoff_entry_; +} + +std::vector<CoreAccountId> FakeProfileOAuth2TokenServiceDelegate::GetAccounts() + const { + std::vector<CoreAccountId> account_ids; + for (const auto& token : refresh_tokens_) + account_ids.push_back(token.first); + return account_ids; +} + +void FakeProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() { + std::vector<CoreAccountId> account_ids = GetAccounts(); + for (const auto& account : account_ids) + RevokeCredentials(account); +} + +void FakeProfileOAuth2TokenServiceDelegate::LoadCredentials( + const CoreAccountId& primary_account_id) { + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + FireRefreshTokensLoaded(); +} + +void FakeProfileOAuth2TokenServiceDelegate::UpdateCredentials( + const CoreAccountId& account_id, + const std::string& refresh_token) { + IssueRefreshTokenForUser(account_id, refresh_token); +} + +void FakeProfileOAuth2TokenServiceDelegate::IssueRefreshTokenForUser( + const CoreAccountId& account_id, + const std::string& token) { + ScopedBatchChange batch(this); + if (token.empty()) { + refresh_tokens_.erase(account_id); + FireRefreshTokenRevoked(account_id); + } else { + refresh_tokens_[account_id].reset(new AccountInfo(token)); + // If the token is a special "invalid" value, then that means the token was + // rejected by the client and is thus not valid. So set the appropriate + // error in that case. This logic is essentially duplicated from + // MutableProfileOAuth2TokenServiceDelegate. + if (token == GaiaConstants::kInvalidRefreshToken) { + refresh_tokens_[account_id]->error = + GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_CLIENT); + } + FireRefreshTokenAvailable(account_id); + } +} + +void FakeProfileOAuth2TokenServiceDelegate::RevokeCredentials( + const CoreAccountId& account_id) { + IssueRefreshTokenForUser(account_id, std::string()); +} + +void FakeProfileOAuth2TokenServiceDelegate::ExtractCredentials( + ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) { + auto it = refresh_tokens_.find(account_id); + DCHECK(it != refresh_tokens_.end()); + to_service->GetDelegate()->UpdateCredentials(account_id, + it->second->refresh_token); + RevokeCredentials(account_id); +} + +scoped_refptr<network::SharedURLLoaderFactory> +FakeProfileOAuth2TokenServiceDelegate::GetURLLoaderFactory() const { + return shared_factory_; +} + +bool FakeProfileOAuth2TokenServiceDelegate::FixRequestErrorIfPossible() { + return fix_request_if_possible_; +} + +void FakeProfileOAuth2TokenServiceDelegate::UpdateAuthError( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + backoff_entry_.InformOfRequest(!error.IsTransientError()); + // Drop transient errors to match OAuth2TokenService's stated contract for + // GetAuthError() and to allow clients to test proper behavior in the case of + // transient errors. + if (error.IsTransientError()) + return; + + if (GetAuthError(account_id) == error) + return; + + auto it = refresh_tokens_.find(account_id); + DCHECK(it != refresh_tokens_.end()); + it->second->error = error; + FireAuthErrorChanged(account_id, error); +} diff --git a/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h new file mode 100644 index 00000000000..f47aa8ff5f2 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h @@ -0,0 +1,87 @@ +// Copyright 2015 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_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" + +namespace network { +class SharedURLLoaderFactory; +} + +class FakeProfileOAuth2TokenServiceDelegate + : public ProfileOAuth2TokenServiceDelegate { + public: + FakeProfileOAuth2TokenServiceDelegate(); + ~FakeProfileOAuth2TokenServiceDelegate() override; + + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) override; + + // Overriden to make sure it works on Android. + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override; + + GoogleServiceAuthError GetAuthError( + const CoreAccountId& account_id) const override; + void UpdateAuthError(const CoreAccountId& account_id, + const GoogleServiceAuthError& error) override; + std::vector<CoreAccountId> GetAccounts() const override; + void RevokeAllCredentials() override; + void LoadCredentials(const CoreAccountId& primary_account_id) override; + void UpdateCredentials(const CoreAccountId& account_id, + const std::string& refresh_token) override; + void RevokeCredentials(const CoreAccountId& account_id) override; + void ExtractCredentials(ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) override; + + scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() + const override; + + bool FixRequestErrorIfPossible() override; + + std::string GetRefreshToken(const CoreAccountId& account_id) const; + + network::TestURLLoaderFactory* test_url_loader_factory() { + return &test_url_loader_factory_; + } + + void set_fix_request_if_possible(bool value) { + fix_request_if_possible_ = value; + } + + const net::BackoffEntry* BackoffEntry() const override; + + private: + struct AccountInfo { + AccountInfo(const std::string& refresh_token); + + const std::string refresh_token; + GoogleServiceAuthError error; + }; + + void IssueRefreshTokenForUser(const CoreAccountId& account_id, + const std::string& token); + + // Maps account ids to info. + std::map<CoreAccountId, std::unique_ptr<AccountInfo>> refresh_tokens_; + + network::TestURLLoaderFactory test_url_loader_factory_; + scoped_refptr<network::SharedURLLoaderFactory> shared_factory_; + bool fix_request_if_possible_ = false; + + net::BackoffEntry backoff_entry_; + + DISALLOW_COPY_AND_ASSIGN(FakeProfileOAuth2TokenServiceDelegate); +}; +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_FAKE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc index 7f95e128b2f..6cddb7a2071 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.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/core/browser/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" #include <stddef.h> @@ -22,13 +22,13 @@ #include "base/time/time.h" #include "base/values.h" #include "build/build_config.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/set_accounts_in_cookie_result.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/ubertoken_fetcher_impl.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_helper.h" +#include "components/signin/internal/identity_manager/ubertoken_fetcher_impl.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" -#include "google_apis/gaia/oauth2_token_service.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/cookies/cookie_change_dispatcher.h" @@ -38,33 +38,11 @@ #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/simple_url_loader.h" -#if defined(OS_CHROMEOS) -#include "chromeos/constants/chromeos_switches.h" -#endif - -namespace signin { -MultiloginParameters::MultiloginParameters( - const gaia::MultiloginMode mode, - const std::vector<std::string>& accounts_to_send) - : mode(mode), accounts_to_send(accounts_to_send) {} - -MultiloginParameters::~MultiloginParameters() {} - -MultiloginParameters::MultiloginParameters(const MultiloginParameters& other) { - mode = other.mode; - accounts_to_send = other.accounts_to_send; -} - -MultiloginParameters& MultiloginParameters::operator=( - const MultiloginParameters& other) { - mode = other.mode; - accounts_to_send = other.accounts_to_send; - return *this; -} -} // namespace signin - namespace { +// The maximum number of retries for a fetcher used in this class. +constexpr int kMaxFetcherRetries = 8; + // In case of an error while fetching using the GaiaAuthFetcher or // SimpleURLLoader, retry with exponential backoff. Try up to 7 times within 15 // minutes. @@ -145,27 +123,26 @@ void RecordListAccountsRetryResult(GoogleServiceAuthError error, GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest( GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, gaia::GaiaSource source) - : request_type_(request_type), account_ids_(account_ids), source_(source) {} + : request_type_(request_type), source_(source) {} GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest( GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, + const std::vector<AccountIdGaiaIdPair>& accounts, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback callback) : request_type_(request_type), - account_ids_(account_ids), + accounts_(accounts), source_(source), set_accounts_in_cookie_completed_callback_(std::move(callback)) {} GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest( GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback callback) : request_type_(request_type), - account_ids_(account_ids), + account_id_(account_id), source_(source), add_account_to_cookie_completed_callback_(std::move(callback)) {} @@ -178,10 +155,16 @@ GaiaCookieManagerService::GaiaCookieRequest& GaiaCookieManagerService::GaiaCookieRequest::operator=(GaiaCookieRequest&&) = default; -const std::string GaiaCookieManagerService::GaiaCookieRequest::GetAccountID() { +const CoreAccountId +GaiaCookieManagerService::GaiaCookieRequest::GetAccountID() { DCHECK_EQ(request_type_, GaiaCookieRequestType::ADD_ACCOUNT); - DCHECK_EQ(1u, account_ids_.size()); - return account_ids_[0]; + DCHECK_EQ(0u, accounts_.size()); + return account_id_; +} + +void GaiaCookieManagerService::GaiaCookieRequest::SetSourceSuffix( + std::string suffix) { + source_.SetGaiaSourceSuffix(suffix); } void GaiaCookieManagerService::GaiaCookieRequest:: @@ -193,7 +176,7 @@ void GaiaCookieManagerService::GaiaCookieRequest:: void GaiaCookieManagerService::GaiaCookieRequest:: RunAddAccountToCookieCompletedCallback( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { if (add_account_to_cookie_completed_callback_) std::move(add_account_to_cookie_completed_callback_).Run(account_id, error); @@ -202,22 +185,22 @@ void GaiaCookieManagerService::GaiaCookieRequest:: // static GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateAddAccountRequest( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback callback) { return GaiaCookieManagerService::GaiaCookieRequest( - GaiaCookieRequestType::ADD_ACCOUNT, {account_id}, source, + GaiaCookieRequestType::ADD_ACCOUNT, account_id, source, std::move(callback)); } // static GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateSetAccountsRequest( - const std::vector<std::string>& account_ids, + const std::vector<AccountIdGaiaIdPair>& accounts, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback callback) { return GaiaCookieManagerService::GaiaCookieRequest( - GaiaCookieRequestType::SET_ACCOUNTS, account_ids, source, + GaiaCookieRequestType::SET_ACCOUNTS, accounts, source, std::move(callback)); } @@ -226,14 +209,14 @@ GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateLogOutRequest( gaia::GaiaSource source) { return GaiaCookieManagerService::GaiaCookieRequest( - GaiaCookieRequestType::LOG_OUT, {}, source); + GaiaCookieRequestType::LOG_OUT, source); } // static GaiaCookieManagerService::GaiaCookieRequest GaiaCookieManagerService::GaiaCookieRequest::CreateListAccountsRequest() { return GaiaCookieManagerService::GaiaCookieRequest( - GaiaCookieRequestType::LIST_ACCOUNTS, {}, gaia::GaiaSource::kChrome); + GaiaCookieRequestType::LIST_ACCOUNTS, gaia::GaiaSource::kChrome); } GaiaCookieManagerService::ExternalCcResultFetcher::ExternalCcResultFetcher( @@ -451,17 +434,17 @@ void GaiaCookieManagerService::ExternalCcResultFetcher:: } GaiaCookieManagerService::GaiaCookieManagerService( - OAuth2TokenService* token_service, + ProfileOAuth2TokenService* token_service, SigninClient* signin_client) : token_service_(token_service), signin_client_(signin_client), external_cc_result_fetcher_(this), fetcher_backoff_(&kBackoffPolicy), fetcher_retries_(0), + listAccountsUnexpectedServerResponseRetried_(false), cookie_listener_binding_(this), external_cc_result_fetched_(false), - list_accounts_stale_(true), - weak_ptr_factory_(this) {} + list_accounts_stale_(true) {} GaiaCookieManagerService::~GaiaCookieManagerService() { CancelAll(); @@ -493,15 +476,17 @@ void GaiaCookieManagerService::Shutdown() { } void GaiaCookieManagerService::SetAccountsInCookie( - const std::vector<std::string>& account_ids, + const std::vector<AccountIdGaiaIdPair>& accounts, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback set_accounts_in_cookies_completed_callback) { + std::vector<std::string> account_ids; + for (const auto& id : accounts) + account_ids.push_back(id.first.id); VLOG(1) << "GaiaCookieManagerService::SetAccountsInCookie: " << base::JoinString(account_ids, " "); requests_.push_back(GaiaCookieRequest::CreateSetAccountsRequest( - account_ids, source, - std::move(set_accounts_in_cookies_completed_callback))); + accounts, source, std::move(set_accounts_in_cookies_completed_callback))); if (!signin_client_->AreSigninCookiesAllowed()) { OnSetAccountsFinished(signin::SetAccountsInCookieResult::kPersistentError); return; @@ -513,7 +498,7 @@ void GaiaCookieManagerService::SetAccountsInCookie( } void GaiaCookieManagerService::AddAccountToCookieInternal( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback) { DCHECK(!account_id.empty()); @@ -535,7 +520,7 @@ void GaiaCookieManagerService::AddAccountToCookieInternal( } void GaiaCookieManagerService::AddAccountToCookie( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback) { VLOG(1) << "GaiaCookieManagerService::AddAccountToCookie: " << account_id; @@ -545,7 +530,7 @@ void GaiaCookieManagerService::AddAccountToCookie( } void GaiaCookieManagerService::AddAccountToCookieWithToken( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& access_token, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback) { @@ -579,6 +564,7 @@ bool GaiaCookieManagerService::ListAccounts( void GaiaCookieManagerService::TriggerListAccounts() { if (requests_.empty()) { fetcher_retries_ = 0; + listAccountsUnexpectedServerResponseRetried_ = false; requests_.push_back(GaiaCookieRequest::CreateListAccountsRequest()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, @@ -651,14 +637,6 @@ void GaiaCookieManagerService::LogOutAllAccounts(gaia::GaiaSource source) { } } -void GaiaCookieManagerService::AddObserver(Observer* observer) { - observer_list_.AddObserver(observer); -} - -void GaiaCookieManagerService::RemoveObserver(Observer* observer) { - observer_list_.RemoveObserver(observer); -} - void GaiaCookieManagerService::CancelAll() { VLOG(1) << "GaiaCookieManagerService::CancelAll"; gaia_auth_fetcher_.reset(); @@ -692,8 +670,8 @@ void GaiaCookieManagerService::OnCookieChange( if (cause == network::mojom::CookieChangeCause::EXPLICIT) { DCHECK(net::CookieChangeCauseIsDeletion(net::CookieChangeCause::EXPLICIT)); - for (auto& observer : observer_list_) { - observer.OnGaiaCookieDeletedByUserAction(); + if (gaia_cookie_deleted_by_user_action_callback_) { + gaia_cookie_deleted_by_user_action_callback_.Run(); } } @@ -705,6 +683,7 @@ void GaiaCookieManagerService::OnCookieChange( if (requests_.empty()) { requests_.push_back(GaiaCookieRequest::CreateListAccountsRequest()); fetcher_retries_ = 0; + listAccountsUnexpectedServerResponseRetried_ = false; signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, weak_ptr_factory_.GetWeakPtr())); @@ -741,12 +720,24 @@ void GaiaCookieManagerService::SignalSetAccountsComplete( requests_.front().RunSetAccountsInCookieCompletedCallback(result); } +void GaiaCookieManagerService::SetGaiaAccountsInCookieUpdatedCallback( + GaiaAccountsInCookieUpdatedCallback callback) { + DCHECK(!gaia_accounts_updated_in_cookie_callback_); + gaia_accounts_updated_in_cookie_callback_ = std::move(callback); +} + +void GaiaCookieManagerService::SetGaiaCookieDeletedByUserActionCallback( + GaiaCookieDeletedByUserActionCallback callback) { + DCHECK(!gaia_cookie_deleted_by_user_action_callback_); + gaia_cookie_deleted_by_user_action_callback_ = std::move(callback); +} + void GaiaCookieManagerService::OnUbertokenFetchComplete( GoogleServiceAuthError error, const std::string& uber_token) { if (error != GoogleServiceAuthError::AuthErrorNone()) { // Note that the UberToken fetcher already retries transient errors. - const std::string account_id = requests_.front().GetAccountID(); + const CoreAccountId account_id = requests_.front().GetAccountID(); VLOG(1) << "Failed to retrieve ubertoken" << " account=" << account_id << " error=" << error.ToString(); SignalAddToCookieComplete(requests_.begin(), error); @@ -775,7 +766,7 @@ void GaiaCookieManagerService::OnUbertokenFetchComplete( } void GaiaCookieManagerService::OnMergeSessionSuccess(const std::string& data) { - const std::string account_id = requests_.front().GetAccountID(); + const CoreAccountId account_id = requests_.front().GetAccountID(); VLOG(1) << "MergeSession successful account=" << account_id; DCHECK(requests_.front().request_type() == GaiaCookieRequestType::ADD_ACCOUNT); @@ -793,11 +784,10 @@ void GaiaCookieManagerService::OnMergeSessionFailure( const GoogleServiceAuthError& error) { DCHECK(requests_.front().request_type() == GaiaCookieRequestType::ADD_ACCOUNT); - const std::string account_id = requests_.front().GetAccountID(); + const CoreAccountId account_id = requests_.front().GetAccountID(); VLOG(1) << "Failed MergeSession" << " account=" << account_id << " error=" << error.ToString(); - if (++fetcher_retries_ < signin::kMaxFetcherRetries && - error.IsTransientError()) { + if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) { fetcher_backoff_.InformOfRequest(false); UMA_HISTOGRAM_ENUMERATION("OAuth2Login.MergeSessionRetry", error.state(), GoogleServiceAuthError::NUM_STATES); @@ -830,7 +820,6 @@ void GaiaCookieManagerService::OnListAccountsSuccess(const std::string& data) { signed_out_accounts_.clear(); GoogleServiceAuthError error( GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE); - RecordListAccountsFailure(error.state()); OnListAccountsFailure(error); return; } @@ -851,8 +840,8 @@ void GaiaCookieManagerService::OnListAccountsSuccess(const std::string& data) { // services, in response to OnGaiaAccountsInCookieUpdated, may try in return // to call ListAccounts, which would immediately return false if the // ListAccounts request is still sitting in queue. - for (auto& observer : observer_list_) { - observer.OnGaiaAccountsInCookieUpdated( + if (gaia_accounts_updated_in_cookie_callback_) { + gaia_accounts_updated_in_cookie_callback_.Run( listed_accounts_, signed_out_accounts_, GoogleServiceAuthError(GoogleServiceAuthError::NONE)); } @@ -865,8 +854,16 @@ void GaiaCookieManagerService::OnListAccountsFailure( GaiaCookieRequestType::LIST_ACCOUNTS); RecordListAccountsRetryResult(error, fetcher_retries_); - if (++fetcher_retries_ < signin::kMaxFetcherRetries && - error.IsTransientError()) { + bool should_retry = + (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) || + (!listAccountsUnexpectedServerResponseRetried_ && + error.state() == GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE); + if (should_retry) { + if (error.state() == GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE) { + listAccountsUnexpectedServerResponseRetried_ = true; + requests_.front().SetSourceSuffix( + GaiaConstants::kUnexpectedServiceResponse); + } fetcher_backoff_.InformOfRequest(false); UMA_HISTOGRAM_ENUMERATION("Signin.ListAccountsRetry", error.state(), GoogleServiceAuthError::NUM_STATES); @@ -880,10 +877,12 @@ void GaiaCookieManagerService::OnListAccountsFailure( } RecordListAccountsFailure(error.state()); - for (auto& observer : observer_list_) { - observer.OnGaiaAccountsInCookieUpdated(listed_accounts_, - signed_out_accounts_, error); + + if (gaia_accounts_updated_in_cookie_callback_) { + gaia_accounts_updated_in_cookie_callback_.Run(listed_accounts_, + signed_out_accounts_, error); } + HandleNextRequest(); } @@ -903,7 +902,7 @@ void GaiaCookieManagerService::OnLogOutFailure( VLOG(1) << "GaiaCookieManagerService::OnLogOutFailure"; RecordLogoutRequestState(LogoutRequestState::kFailed); - if (++fetcher_retries_ < signin::kMaxFetcherRetries) { + if (++fetcher_retries_ < kMaxFetcherRetries) { fetcher_backoff_.InformOfRequest(false); fetcher_timer_.Start( FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), @@ -918,7 +917,7 @@ void GaiaCookieManagerService::OnLogOutFailure( } void GaiaCookieManagerService::StartFetchingUbertoken() { - const std::string account_id = requests_.front().GetAccountID(); + const CoreAccountId account_id = requests_.front().GetAccountID(); VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id=" << requests_.front().GetAccountID(); uber_token_fetcher_ = std::make_unique<signin::UbertokenFetcherImpl>( @@ -956,17 +955,7 @@ void GaiaCookieManagerService::StartFetchingLogOut() { RecordLogoutRequestState(LogoutRequestState::kStarted); gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source()); - bool use_continue_url = false; -#if defined(OS_ANDROID) - use_continue_url = base::FeatureList::IsEnabled(signin::kMiceFeature); -#elif defined(OS_CHROMEOS) - use_continue_url = chromeos::switches::IsAccountManagerEnabled(); -#endif - if (use_continue_url) { - gaia_auth_fetcher_->StartLogOutWithBlankContinueURL(); - } else { - gaia_auth_fetcher_->StartLogOut(); - } + gaia_auth_fetcher_->StartLogOut(); } void GaiaCookieManagerService::StartFetchingListAccounts() { @@ -980,7 +969,7 @@ void GaiaCookieManagerService::StartSetAccounts() { DCHECK(!requests_.empty()); DCHECK_EQ(GaiaCookieRequestType::SET_ACCOUNTS, requests_.front().request_type()); - DCHECK(!requests_.front().account_ids().empty()); + DCHECK(!requests_.front().accounts().empty()); if (!external_cc_result_fetched_ && !external_cc_result_fetcher_.IsRunning()) { @@ -991,7 +980,7 @@ void GaiaCookieManagerService::StartSetAccounts() { } oauth_multilogin_helper_ = std::make_unique<signin::OAuthMultiloginHelper>( - signin_client_, token_service_, requests_.front().account_ids(), + signin_client_, token_service_, requests_.front().accounts(), external_cc_result_fetcher_.GetExternalCcResult(), base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished, weak_ptr_factory_.GetWeakPtr())); @@ -1027,22 +1016,24 @@ void GaiaCookieManagerService::HandleNextRequest() { } else { switch (requests_.front().request_type()) { case GaiaCookieRequestType::ADD_ACCOUNT: - DCHECK_EQ(1u, requests_.front().account_ids().size()); + DCHECK_EQ(0u, requests_.front().accounts().size()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingUbertoken, weak_ptr_factory_.GetWeakPtr())); break; - case GaiaCookieRequestType::SET_ACCOUNTS: + case GaiaCookieRequestType::SET_ACCOUNTS: { StartSetAccounts(); break; + } case GaiaCookieRequestType::LOG_OUT: - DCHECK(requests_.front().account_ids().empty()); + DCHECK(requests_.front().accounts().empty()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut, weak_ptr_factory_.GetWeakPtr())); break; case GaiaCookieRequestType::LIST_ACCOUNTS: - DCHECK(requests_.front().account_ids().empty()); + listAccountsUnexpectedServerResponseRetried_ = false; + DCHECK(requests_.front().accounts().empty()); uber_token_fetcher_.reset(); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h index 94901f4e0a5..7886970f94d 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.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_CORE_BROWSER_GAIA_COOKIE_MANAGER_SERVICE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_GAIA_COOKIE_MANAGER_SERVICE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_GAIA_COOKIE_MANAGER_SERVICE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_GAIA_COOKIE_MANAGER_SERVICE_H_ #include <map> #include <memory> @@ -18,12 +18,11 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/timer/timer.h" -#include "components/signin/core/browser/oauth_multilogin_helper.h" -#include "components/signin/core/browser/signin_client.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/signin_client.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_util.h" -#include "google_apis/gaia/oauth2_token_service.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/base/backoff_entry.h" #include "services/network/public/mojom/cookie_manager.mojom.h" @@ -39,27 +38,10 @@ class SimpleURLLoader; namespace signin { +class OAuthMultiloginHelper; class UbertokenFetcherImpl; enum class SetAccountsInCookieResult; -// The maximum number of retries for a fetcher used in this class. -constexpr int kMaxFetcherRetries = 8; - -struct MultiloginParameters { - MultiloginParameters(gaia::MultiloginMode mode, - const std::vector<std::string>& accounts_to_send); - MultiloginParameters(const MultiloginParameters& other); - MultiloginParameters& operator=(const MultiloginParameters& other); - ~MultiloginParameters(); - - // Needed for testing. - bool operator==(const MultiloginParameters& other) const { - return mode == other.mode && accounts_to_send == other.accounts_to_send; - } - - gaia::MultiloginMode mode; - std::vector<std::string> accounts_to_send; -}; } // namespace signin // Merges a Google account known to Chrome into the cookie jar. When merging @@ -73,6 +55,8 @@ struct MultiloginParameters { class GaiaCookieManagerService : public GaiaAuthConsumer, public network::mojom::CookieChangeListener { public: + using AccountIdGaiaIdPair = std::pair<CoreAccountId, std::string>; + enum GaiaCookieRequestType { ADD_ACCOUNT, LOG_OUT, @@ -82,10 +66,16 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, typedef base::OnceCallback<void(signin::SetAccountsInCookieResult)> SetAccountsInCookieCompletedCallback; - typedef base::OnceCallback<void(const std::string&, + typedef base::OnceCallback<void(const CoreAccountId&, const GoogleServiceAuthError&)> AddAccountToCookieCompletedCallback; + typedef base::RepeatingCallback<void(const std::vector<gaia::ListedAccount>&, + const std::vector<gaia::ListedAccount>&, + const GoogleServiceAuthError&)> + GaiaAccountsInCookieUpdatedCallback; + typedef base::RepeatingCallback<void()> GaiaCookieDeletedByUserActionCallback; + // Contains the information and parameters for any request. class GaiaCookieRequest { public: @@ -94,45 +84,50 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, GaiaCookieRequest& operator=(GaiaCookieRequest&&); GaiaCookieRequestType request_type() const { return request_type_; } - const std::vector<std::string>& account_ids() const { return account_ids_; } + const std::vector<AccountIdGaiaIdPair>& accounts() const { + return accounts_; + } // For use in the Request of type ADD_ACCOUNT which must have exactly one - // account_id in the array. It checks this condition and extracts this one - // account. - const std::string GetAccountID(); + // account_id. + const CoreAccountId GetAccountID(); gaia::GaiaSource source() const { return source_; } + // Sets GaiaSource suffix. + void SetSourceSuffix(std::string suffix); void RunSetAccountsInCookieCompletedCallback( signin::SetAccountsInCookieResult result); void RunAddAccountToCookieCompletedCallback( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error); static GaiaCookieRequest CreateAddAccountRequest( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback callback); static GaiaCookieRequest CreateLogOutRequest(gaia::GaiaSource source); static GaiaCookieRequest CreateListAccountsRequest(); static GaiaCookieRequest CreateSetAccountsRequest( - const std::vector<std::string>& account_ids, + const std::vector<AccountIdGaiaIdPair>& account_ids, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback callback); private: GaiaCookieRequest(GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, gaia::GaiaSource source); GaiaCookieRequest(GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, + const std::vector<AccountIdGaiaIdPair>& accounts, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback callback); GaiaCookieRequest(GaiaCookieRequestType request_type, - const std::vector<std::string>& account_ids, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback callback); GaiaCookieRequestType request_type_; - std::vector<std::string> account_ids_; + // For use in the request of type ADD_ACCOUNT. + CoreAccountId account_id_; + // For use in the request of type SET_ACCOUNT. + std::vector<AccountIdGaiaIdPair> accounts_; gaia::GaiaSource source_; SetAccountsInCookieCompletedCallback @@ -143,28 +138,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, DISALLOW_COPY_AND_ASSIGN(GaiaCookieRequest); }; - class Observer { - public: - // Called whenever the GaiaCookieManagerService's list of GAIA accounts is - // updated. The GCMS monitors the APISID cookie and triggers a /ListAccounts - // call on change. The GCMS will also call ListAccounts upon the first call - // to ListAccounts(). The GCMS will delay calling ListAccounts if other - // requests are in queue that would modify the APISID cookie. - // If the ListAccounts call fails and the GCMS cannot recover, the reason - // is passed in |error|. - virtual void OnGaiaAccountsInCookieUpdated( - const std::vector<gaia::ListedAccount>& accounts, - const std::vector<gaia::ListedAccount>& signed_out_accounts, - const GoogleServiceAuthError& error) {} - - // Called when the Gaia cookie has been deleted explicitly by a user action, - // e.g. from the settings or by an extension. - virtual void OnGaiaCookieDeletedByUserAction() {} - - protected: - virtual ~Observer() {} - }; - // Class to retrieve the external connection check results from gaia. // Declared publicly for unit tests. class ExternalCcResultFetcher : public GaiaAuthConsumer { @@ -226,7 +199,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, DISALLOW_COPY_AND_ASSIGN(ExternalCcResultFetcher); }; - GaiaCookieManagerService(OAuth2TokenService* token_service, + GaiaCookieManagerService(ProfileOAuth2TokenService* token_service, SigninClient* signin_client); ~GaiaCookieManagerService() override; @@ -235,11 +208,11 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void Shutdown(); void AddAccountToCookie( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback); void AddAccountToCookieWithToken( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& access_token, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback); @@ -247,7 +220,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // Takes list of account_ids and sets the cookie for these accounts regardless // of the current cookie state. Removes the accounts that are not in // account_ids and add the missing ones. - void SetAccountsInCookie(const std::vector<std::string>& account_ids, + void SetAccountsInCookie(const std::vector<AccountIdGaiaIdPair>& account_ids, gaia::GaiaSource source, SetAccountsInCookieCompletedCallback set_accounts_in_cookies_completed_callback); @@ -270,10 +243,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // service. Virtual for testing. virtual void ForceOnCookieChangeProcessing(); - // Add or remove observers of this helper. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - // Cancel all login requests. void CancelAll(); @@ -295,7 +264,26 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, list_accounts_stale_ = stale; } - // Returns a non-NULL pointer to its instance of net::BackoffEntry + // If set, this callback will be invoked whenever the + // GaiaCookieManagerService's list of GAIA accounts is updated. The GCMS + // monitors the APISID cookie and triggers a /ListAccounts call on change. + // The GCMS will also call ListAccounts upon the first call to + // ListAccounts(). The GCMS will delay calling ListAccounts if other + // requests are in queue that would modify the APISID cookie. + // If the ListAccounts call fails and the GCMS cannot recover, the reason + // is passed in |error|. + // This method can only be called once. + void SetGaiaAccountsInCookieUpdatedCallback( + GaiaAccountsInCookieUpdatedCallback callback); + + // If set, this callback will be invoked whenever the Gaia cookie has + // been deleted explicitly by a user action, e.g. from the settings or by an + // extension. + // This method can only be called once. + void SetGaiaCookieDeletedByUserActionCallback( + GaiaCookieDeletedByUserActionCallback callback); + + // Returns a non-null pointer to its instance of net::BackoffEntry const net::BackoffEntry* GetBackoffEntry() { return &fetcher_backoff_; } // Ubertoken fetch completion callback. Called by unittests directly. @@ -334,7 +322,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // Helper method for AddAccountToCookie* methods. void AddAccountToCookieInternal( - const std::string& account_id, + const CoreAccountId& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback); @@ -361,9 +349,13 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // Start the next request, if needed. void HandleNextRequest(); - OAuth2TokenService* token_service_; + ProfileOAuth2TokenService* token_service_; SigninClient* signin_client_; + GaiaAccountsInCookieUpdatedCallback gaia_accounts_updated_in_cookie_callback_; + GaiaCookieDeletedByUserActionCallback + gaia_cookie_deleted_by_user_action_callback_; + std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_; ExternalCcResultFetcher external_cc_result_fetcher_; @@ -375,6 +367,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, base::OneShotTimer fetcher_timer_; int fetcher_retries_; + // If list accounts retried after a failure because of getting an unexpected + // service response. + bool listAccountsUnexpectedServerResponseRetried_; + // The last fetched ubertoken, for use in MergeSession retries. std::string uber_token_; @@ -389,10 +385,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // executed at a time. base::circular_deque<GaiaCookieRequest> requests_; - // List of observers to notify when merge session completes. - // Makes sure list is empty on destruction. - base::ObserverList<Observer, true>::Unchecked observer_list_; - // True once the ExternalCCResultFetcher has completed once. bool external_cc_result_fetched_; @@ -401,9 +393,9 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, bool list_accounts_stale_; - base::WeakPtrFactory<GaiaCookieManagerService> weak_ptr_factory_; + base::WeakPtrFactory<GaiaCookieManagerService> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(GaiaCookieManagerService); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_GAIA_COOKIE_MANAGER_SERVICE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_GAIA_COOKIE_MANAGER_SERVICE_H_ diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc index 85a5c442b00..492ee78b725 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_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/core/browser/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" #include <algorithm> #include <memory> @@ -21,12 +21,11 @@ #include "base/test/scoped_task_environment.h" #include "base/test/test_mock_time_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "services/network/test/test_url_loader_factory.h" @@ -35,25 +34,26 @@ namespace { +const char kAccountId1[] = "acc1@gmail.com"; +const char kAccountId2[] = "acc2@gmail.com"; +const char kAccountId3[] = "acc3@gmail.com"; + using MockAddAccountToCookieCompletedCallback = base::MockCallback< GaiaCookieManagerService::AddAccountToCookieCompletedCallback>; -class MockObserver : public GaiaCookieManagerService::Observer { +class MockObserver { public: - explicit MockObserver(GaiaCookieManagerService* helper) : helper_(helper) { - helper_->AddObserver(this); + explicit MockObserver(GaiaCookieManagerService* helper) { + helper->SetGaiaAccountsInCookieUpdatedCallback(base::BindRepeating( + &MockObserver::OnGaiaAccountsInCookieUpdated, base::Unretained(this))); } - ~MockObserver() override { helper_->RemoveObserver(this); } - MOCK_METHOD3(OnGaiaAccountsInCookieUpdated, void(const std::vector<gaia::ListedAccount>&, const std::vector<gaia::ListedAccount>&, const GoogleServiceAuthError&)); private: - GaiaCookieManagerService* helper_; - DISALLOW_COPY_AND_ASSIGN(MockObserver); }; @@ -94,7 +94,7 @@ MATCHER_P(ListedAccountEquals, expected, "") { class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { public: - InstrumentedGaiaCookieManagerService(OAuth2TokenService* token_service, + InstrumentedGaiaCookieManagerService(ProfileOAuth2TokenService* token_service, SigninClient* signin_client) : GaiaCookieManagerService(token_service, signin_client) { total++; @@ -114,14 +114,19 @@ class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { class GaiaCookieManagerServiceTest : public testing::Test { public: GaiaCookieManagerServiceTest() - : no_error_(GoogleServiceAuthError::NONE), + : account_id1_(kAccountId1), + account_id2_(kAccountId2), + account_id3_(kAccountId3), + no_error_(GoogleServiceAuthError::NONE), error_(GoogleServiceAuthError::SERVICE_ERROR), canceled_(GoogleServiceAuthError::REQUEST_CANCELED) { AccountTrackerService::RegisterPrefs(pref_service_.registry()); - signin_client_.reset(new TestSigninClient(&pref_service_)); + signin_client_ = std::make_unique<TestSigninClient>(&pref_service_); + token_service_ = + std::make_unique<FakeProfileOAuth2TokenService>(&pref_service_); } - OAuth2TokenService* token_service() { return &token_service_; } + ProfileOAuth2TokenService* token_service() { return token_service_.get(); } TestSigninClient* signin_client() { return signin_client_.get(); } void SimulateUbertokenSuccess(GaiaCookieManagerService* gcms, @@ -135,14 +140,14 @@ class GaiaCookieManagerServiceTest : public testing::Test { gcms->OnUbertokenFetchComplete(error, /*uber_token=*/std::string()); } - void SimulateAccessTokenFailure(OAuth2TokenService::Consumer* consumer, - OAuth2TokenService::Request* request, + void SimulateAccessTokenFailure(OAuth2AccessTokenManager::Consumer* consumer, + OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) { consumer->OnGetTokenFailure(request, error); } - void SimulateAccessTokenSuccess(OAuth2TokenService::Consumer* consumer, - OAuth2TokenService::Request* request) { + void SimulateAccessTokenSuccess(OAuth2AccessTokenManager::Consumer* consumer, + OAuth2AccessTokenManager::Request* request) { OAuth2AccessTokenConsumer::TokenResponse token_response = OAuth2AccessTokenConsumer::TokenResponse("AccessToken", base::Time(), "Idtoken"); @@ -217,14 +222,18 @@ class GaiaCookieManagerServiceTest : public testing::Test { return signin_client_->GetURLLoaderFactory(); } + const CoreAccountId account_id1_; + const CoreAccountId account_id2_; + const CoreAccountId account_id3_; + private: base::test::ScopedTaskEnvironment task_environment_; - FakeOAuth2TokenService token_service_; GoogleServiceAuthError no_error_; GoogleServiceAuthError error_; GoogleServiceAuthError canceled_; TestingPrefServiceSimple pref_service_; std::unique_ptr<TestSigninClient> signin_client_; + std::unique_ptr<FakeProfileOAuth2TokenService> token_service_; }; } // namespace @@ -238,10 +247,9 @@ TEST_F(GaiaCookieManagerServiceTest, Success) { EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc1@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token"); } @@ -254,9 +262,9 @@ TEST_F(GaiaCookieManagerServiceTest, FailedMergeSession) { EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, error()); // Persistent error incurs no further retries. @@ -271,10 +279,9 @@ TEST_F(GaiaCookieManagerServiceTest, AddAccountCookiesDisabled) { signin_client()->set_are_signin_cookies_allowed(false); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc1@gmail.com", canceled())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, canceled())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); } @@ -290,10 +297,9 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetried) { EXPECT_CALL(helper, StartFetchingMergeSession()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc1@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, canceled()); DCHECK(helper.is_running()); @@ -315,10 +321,9 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetriedTwice) { EXPECT_CALL(helper, StartFetchingMergeSession()).Times(2); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc1@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, canceled()); DCHECK(helper.is_running()); @@ -339,9 +344,9 @@ TEST_F(GaiaCookieManagerServiceTest, FailedUbertoken) { EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id1_, error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateUbertokenFailure(&helper, error()); } @@ -354,14 +359,12 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; - EXPECT_CALL(add_account_to_cookie_completed1, - Run("acc1@gmail.com", no_error())); - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, no_error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateMergeSessionSuccess(&helper, "token2"); @@ -375,13 +378,12 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure1) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; - EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", error())); - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionFailure(&helper, error()); SimulateMergeSessionSuccess(&helper, "token2"); @@ -395,13 +397,12 @@ TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure2) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; - EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", error())); - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateUbertokenFailure(&helper, error()); SimulateMergeSessionSuccess(&helper, "token2"); @@ -416,14 +417,14 @@ TEST_F(GaiaCookieManagerServiceTest, AllRequestsInMultipleGoes) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run(_, no_error())).Times(4); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); - helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token2"); @@ -443,10 +444,9 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsNoQueue) { EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); @@ -463,10 +463,9 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsFails) { EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); @@ -484,10 +483,9 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterOneAddInQueue) { EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); @@ -504,15 +502,13 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterTwoAddsInQueue) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; - EXPECT_CALL(add_account_to_cookie_completed1, - Run("acc1@gmail.com", no_error())); - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", canceled())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, no_error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, canceled())); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + 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("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); @@ -528,10 +524,9 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsTwice) { EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; - EXPECT_CALL(add_account_to_cookie_completed, - Run("acc2@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed, Run(account_id2_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); @@ -550,17 +545,15 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeAdd) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed2, add_account_to_cookie_completed3; - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", no_error())); - EXPECT_CALL(add_account_to_cookie_completed3, - Run("acc3@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, no_error())); + EXPECT_CALL(add_account_to_cookie_completed3, Run(account_id3_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); - helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateLogOutSuccess(&helper); @@ -577,19 +570,17 @@ TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeLogoutAndAdd) { MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed2, add_account_to_cookie_completed3; - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", no_error())); - EXPECT_CALL(add_account_to_cookie_completed3, - Run("acc3@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, no_error())); + EXPECT_CALL(add_account_to_cookie_completed3, Run(account_id3_, no_error())); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); // Second LogOut will never be fetched. helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); - helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateLogOutSuccess(&helper); @@ -603,27 +594,25 @@ TEST_F(GaiaCookieManagerServiceTest, PendingSigninThenSignout) { // From the first Signin. MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1; - EXPECT_CALL(add_account_to_cookie_completed1, - Run("acc1@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, no_error())); // From the sign out and then re-sign in. EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed3; - EXPECT_CALL(add_account_to_cookie_completed3, - Run("acc3@gmail.com", no_error())); + EXPECT_CALL(add_account_to_cookie_completed3, Run(account_id3_, no_error())); // Total sign in 2 times, not enforcing ordered sequences. EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); - helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id3_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateMergeSessionSuccess(&helper, "token3"); } @@ -635,15 +624,13 @@ TEST_F(GaiaCookieManagerServiceTest, CancelSignIn) { EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; - EXPECT_CALL(add_account_to_cookie_completed1, - Run("acc1@gmail.com", no_error())); - EXPECT_CALL(add_account_to_cookie_completed2, - Run("acc2@gmail.com", canceled())); + EXPECT_CALL(add_account_to_cookie_completed1, Run(account_id1_, no_error())); + EXPECT_CALL(add_account_to_cookie_completed2, Run(account_id2_, canceled())); EXPECT_CALL(helper, StartFetchingLogOut()); - helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id1_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); - helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, + helper.AddAccountToCookie(account_id2_, gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); @@ -891,7 +878,7 @@ TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCC) { EXPECT_CALL(helper, StartFetchingUbertoken()); helper.AddAccountToCookie( - "acc1@gmail.com", gaia::GaiaSource::kChrome, + account_id1_, gaia::GaiaSource::kChrome, GaiaCookieManagerService::AddAccountToCookieCompletedCallback()); ASSERT_FALSE(IsLoadPending()); @@ -917,7 +904,7 @@ TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCCOnce) { EXPECT_CALL(helper, StartFetchingUbertoken()); helper.AddAccountToCookie( - "acc2@gmail.com", gaia::GaiaSource::kChrome, + account_id2_, gaia::GaiaSource::kChrome, GaiaCookieManagerService::AddAccountToCookieCompletedCallback()); // There is already a ExternalCCResultFetch underway. This will trigger // StartFetchingMergeSession. diff --git a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc index b32db5def12..0364fbc2ce2 100644 --- a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.cc +++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.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/core/browser/mutable_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h" #include <stddef.h> @@ -16,11 +16,11 @@ #include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_metrics.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/webdata/token_web_data.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/webdata/token_web_data.h" #include "components/webdata/common/web_data_service_base.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_util.h" @@ -112,15 +112,14 @@ void RecordTokenChanged(const std::string& existing_token, DCHECK(!new_token.empty()); TokenStateTransition transition = TokenStateTransition::kCount; if (existing_token.empty()) { - transition = (new_token == OAuth2TokenServiceDelegate::kInvalidRefreshToken) + transition = (new_token == GaiaConstants::kInvalidRefreshToken) ? TokenStateTransition::kNoneToInvalid : TokenStateTransition::kNoneToRegular; - } else if (existing_token == - OAuth2TokenServiceDelegate::kInvalidRefreshToken) { + } else if (existing_token == GaiaConstants::kInvalidRefreshToken) { transition = TokenStateTransition::kInvalidToRegular; } else { // Existing token is a regular token. - transition = (new_token == OAuth2TokenServiceDelegate::kInvalidRefreshToken) + transition = (new_token == GaiaConstants::kInvalidRefreshToken) ? TokenStateTransition::kRegularToInvalid : TokenStateTransition::kRegularToRegular; } @@ -130,18 +129,16 @@ void RecordTokenChanged(const std::string& existing_token, // Record metrics when a token was loaded. void RecordTokenLoaded(const std::string& token) { - RecordTokenStateTransition( - (token == OAuth2TokenServiceDelegate::kInvalidRefreshToken) - ? TokenStateTransition::kLoadInvalid - : TokenStateTransition::kLoadRegular); + RecordTokenStateTransition((token == GaiaConstants::kInvalidRefreshToken) + ? TokenStateTransition::kLoadInvalid + : TokenStateTransition::kLoadRegular); } // Record metrics when a token was revoked. void RecordTokenRevoked(const std::string& token) { - RecordTokenStateTransition( - (token == OAuth2TokenServiceDelegate::kInvalidRefreshToken) - ? TokenStateTransition::kInvalidToNone - : TokenStateTransition::kRegularToNone); + RecordTokenStateTransition((token == GaiaConstants::kInvalidRefreshToken) + ? TokenStateTransition::kInvalidToNone + : TokenStateTransition::kRegularToNone); } std::string ApplyAccountIdPrefix(const std::string& account_id) { @@ -156,25 +153,26 @@ bool IsLegacyServiceId(const std::string& account_id) { return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0; } -std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) { - return prefixed_account_id.substr(kAccountIdPrefixLength); +CoreAccountId RemoveAccountIdPrefix(const std::string& prefixed_account_id) { + return CoreAccountId(prefixed_account_id.substr(kAccountIdPrefixLength)); } -OAuth2TokenServiceDelegate::LoadCredentialsState -LoadCredentialsStateFromTokenResult(TokenServiceTable::Result token_result) { +signin::LoadCredentialsState LoadCredentialsStateFromTokenResult( + TokenServiceTable::Result token_result) { switch (token_result) { case TokenServiceTable::TOKEN_DB_RESULT_SQL_INVALID_STATEMENT: case TokenServiceTable::TOKEN_DB_RESULT_BAD_ENTRY: - return OAuth2TokenServiceDelegate:: + return signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS; case TokenServiceTable::TOKEN_DB_RESULT_DECRYPT_ERROR: - return OAuth2TokenServiceDelegate:: + return signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS; case TokenServiceTable::TOKEN_DB_RESULT_SUCCESS: - return OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS; + return signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS; } NOTREACHED(); - return OAuth2TokenServiceDelegate:: + return signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS; } @@ -203,7 +201,7 @@ bool ShouldMigrateToDice(signin::AccountConsistencyMethod account_consistency, // Do not migrate if some accounts are not valid. for (auto iter = db_tokens.begin(); iter != db_tokens.end(); ++iter) { const std::string& prefixed_account_id = iter->first; - std::string account_id = RemoveAccountIdPrefix(prefixed_account_id); + CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id); AccountInfo account_info = account_tracker->GetAccountInfo(account_id); if (!account_info.IsValid()) { return false; @@ -240,7 +238,7 @@ class MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken GaiaAuthFetcher fetcher_; std::string refresh_token_; int attempt_; - base::WeakPtrFactory<RevokeServerRefreshToken> weak_ptr_factory_; + base::WeakPtrFactory<RevokeServerRefreshToken> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken); }; @@ -256,8 +254,7 @@ MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: gaia::GaiaSource::kChrome, token_service_delegate_->GetURLLoaderFactory()), refresh_token_(refresh_token), - attempt_(attempt), - weak_ptr_factory_(this) { + attempt_(attempt) { RecordRefreshTokenRevocationRequestEvent( TokenRevocationRequestProgress::kRequestCreated); client->DelayNetworkCall( @@ -372,9 +369,9 @@ void MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs( registry->RegisterBooleanPref(prefs::kTokenServiceDiceCompatible, false); } -OAuth2AccessTokenFetcher* +std::unique_ptr<OAuth2AccessTokenFetcher> MutableProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher( - const std::string& account_id, + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) { ValidateAccountId(account_id); @@ -382,29 +379,30 @@ MutableProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher( if (refresh_tokens_[account_id].last_auth_error.IsPersistentError()) { VLOG(1) << "Request for token has been rejected due to persistent error #" << refresh_tokens_[account_id].last_auth_error.state(); - return new OAuth2AccessTokenFetcherImmediateError( + return std::make_unique<OAuth2AccessTokenFetcherImmediateError>( consumer, refresh_tokens_[account_id].last_auth_error); } if (backoff_entry_.ShouldRejectRequest()) { VLOG(1) << "Request for token has been rejected due to backoff rules from" << " previous error #" << backoff_error_.state(); - return new OAuth2AccessTokenFetcherImmediateError(consumer, backoff_error_); + return std::make_unique<OAuth2AccessTokenFetcherImmediateError>( + consumer, backoff_error_); } std::string refresh_token = GetRefreshToken(account_id); DCHECK(!refresh_token.empty()); - return new OAuth2AccessTokenFetcherImpl(consumer, url_loader_factory, - refresh_token); + return std::make_unique<OAuth2AccessTokenFetcherImpl>( + consumer, url_loader_factory, refresh_token); } GoogleServiceAuthError MutableProfileOAuth2TokenServiceDelegate::GetAuthError( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto it = refresh_tokens_.find(account_id); return (it == refresh_tokens_.end()) ? GoogleServiceAuthError::AuthErrorNone() : it->second.last_auth_error; } void MutableProfileOAuth2TokenServiceDelegate::UpdateAuthError( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { VLOG(1) << "MutablePO2TS::UpdateAuthError. Error: " << error.state() << " account_id=" << account_id; @@ -436,7 +434,7 @@ void MutableProfileOAuth2TokenServiceDelegate::UpdateAuthError( } std::string MutableProfileOAuth2TokenServiceDelegate::GetTokenForMultilogin( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto iter = refresh_tokens_.find(account_id); if (iter == refresh_tokens_.end() || iter->second.last_auth_error != GoogleServiceAuthError::AuthErrorNone()) { @@ -448,13 +446,13 @@ std::string MutableProfileOAuth2TokenServiceDelegate::GetTokenForMultilogin( } bool MutableProfileOAuth2TokenServiceDelegate::RefreshTokenIsAvailable( - const std::string& account_id) const { + const CoreAccountId& account_id) const { VLOG(1) << "MutablePO2TS::RefreshTokenIsAvailable"; return !GetRefreshToken(account_id).empty(); } std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshToken( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto iter = refresh_tokens_.find(account_id); if (iter != refresh_tokens_.end()) { const std::string refresh_token = iter->second.refresh_token; @@ -465,13 +463,13 @@ std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshToken( } std::string MutableProfileOAuth2TokenServiceDelegate::GetRefreshTokenForTest( - const std::string& account_id) const { + const CoreAccountId& account_id) const { return GetRefreshToken(account_id); } -std::vector<std::string> -MutableProfileOAuth2TokenServiceDelegate::GetAccounts() { - std::vector<std::string> account_ids; +std::vector<CoreAccountId> +MutableProfileOAuth2TokenServiceDelegate::GetAccounts() const { + std::vector<CoreAccountId> account_ids; for (auto& token : refresh_tokens_) { account_ids.push_back(token.first); } @@ -484,27 +482,30 @@ MutableProfileOAuth2TokenServiceDelegate::GetURLLoaderFactory() const { } void MutableProfileOAuth2TokenServiceDelegate::InvalidateTokenForMultilogin( - const std::string& failed_account) { + const CoreAccountId& failed_account) { UpdateAuthError( failed_account, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); } void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials( - const std::string& primary_account_id) { - if (load_credentials_state() == LOAD_CREDENTIALS_IN_PROGRESS) { + const CoreAccountId& primary_account_id) { + if (load_credentials_state() == + signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS) { VLOG(1) << "Load credentials operation already in progress"; return; } - set_load_credentials_state(LOAD_CREDENTIALS_IN_PROGRESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS); #if defined(OS_CHROMEOS) // TODO(sinhak): Remove this ifdef block after Account Manager is switched on. // ChromeOS OOBE loads credentials without a primary account and expects this // to be a no-op. See http://crbug.com/891818 if (primary_account_id.empty()) { - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); FinishLoadingCredentials(); return; } @@ -520,7 +521,9 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials( if (!token_web_data_) { // This case only exists in unit tests that do not care about loading // credentials. - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS); + set_load_credentials_state( + signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS); FinishLoadingCredentials(); return; } @@ -528,8 +531,9 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials( // If |account_id| is an email address, then canonicalize it. This is needed // to support legacy account IDs, and will not be needed after switching to // gaia IDs. - if (primary_account_id.find('@') != std::string::npos) { - loading_primary_account_id_ = gaia::CanonicalizeEmail(primary_account_id); + if (primary_account_id.id.find('@') != std::string::npos) { + loading_primary_account_id_ = + CoreAccountId(gaia::CanonicalizeEmail(primary_account_id.id)); } else { loading_primary_account_id_ = primary_account_id; } @@ -555,7 +559,9 @@ void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone( set_load_credentials_state(LoadCredentialsStateFromTokenResult( token_result->GetValue().db_result)); } else { - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS); + set_load_credentials_state( + signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED); } // Make sure that we have an entry for |loading_primary_account_id_| in the @@ -563,11 +569,14 @@ void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone( // while this profile is connected to an account. if (!loading_primary_account_id_.empty() && refresh_tokens_.count(loading_primary_account_id_) == 0) { - if (load_credentials_state() == LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) { + if (load_credentials_state() == + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) { set_load_credentials_state( - LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT); + signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT); } - AddAccountStatus(loading_primary_account_id_, kInvalidRefreshToken, + AddAccountStatus(loading_primary_account_id_, + GaiaConstants::kInvalidRefreshToken, GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_MISSING)); @@ -583,7 +592,7 @@ void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone( } #endif - loading_primary_account_id_.clear(); + loading_primary_account_id_ = CoreAccountId(); FinishLoadingCredentials(); } @@ -616,20 +625,20 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory( } } else { DCHECK(!refresh_token.empty()); - std::string account_id = RemoveAccountIdPrefix(prefixed_account_id); + CoreAccountId account_id = RemoveAccountIdPrefix(prefixed_account_id); switch (migration_state) { case AccountTrackerService::MIGRATION_IN_PROGRESS: { // Migrate to gaia-ids. AccountInfo account_info = - account_tracker_service_->FindAccountInfoByEmail(account_id); - // |account_info.gaia| could be empty if |account_id| is already - // gaia id. This could happen if the chrome was closed in the middle - // of migration. - if (!account_info.gaia.empty()) { + account_tracker_service_->FindAccountInfoByEmail(account_id.id); + // |account_info| can be empty if |account_id| was already migrated. + // This could happen if the chrome was closed in the middle of the + // account id migration. + if (!account_info.IsEmpty()) { ClearPersistedCredentials(account_id); - PersistCredentials(account_info.gaia, refresh_token); - account_id = account_info.gaia; + account_id = account_info.account_id; + PersistCredentials(account_id, refresh_token); } // Skip duplicate accounts, this could happen if migration was @@ -642,24 +651,24 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory( // If the account_id is an email address, then canonicalize it. This // is to support legacy account_ids, and will not be needed after // switching to gaia-ids. - if (account_id.find('@') != std::string::npos) { + if (account_id.id.find('@') != std::string::npos) { // If the canonical account id is not the same as the loaded // account id, make sure not to overwrite a refresh token from // a canonical version. If no canonical version was loaded, then // re-persist this refresh token with the canonical account id. - std::string canon_account_id = - gaia::CanonicalizeEmail(account_id); + CoreAccountId canon_account_id = + CoreAccountId(gaia::CanonicalizeEmail(account_id.id)); if (canon_account_id != account_id) { ClearPersistedCredentials(account_id); - if (db_tokens.count(ApplyAccountIdPrefix(canon_account_id)) == - 0) + if (db_tokens.count( + ApplyAccountIdPrefix(canon_account_id.id)) == 0) PersistCredentials(canon_account_id, refresh_token); } account_id = canon_account_id; } break; case AccountTrackerService::MIGRATION_DONE: - DCHECK_EQ(std::string::npos, account_id.find('@')); + DCHECK_EQ(std::string::npos, account_id.id.find('@')); break; case AccountTrackerService::NUM_MIGRATION_STATES: NOTREACHED(); @@ -694,7 +703,7 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory( if (load_account && revoke_all_tokens_on_load_) { if (account_id == loading_primary_account_id_) { RevokeCredentialsOnServer(refresh_token); - refresh_token = kInvalidRefreshToken; + refresh_token = GaiaConstants::kInvalidRefreshToken; PersistCredentials(account_id, refresh_token); } else { load_account = false; @@ -731,15 +740,13 @@ void MutableProfileOAuth2TokenServiceDelegate::LoadAllCredentialsIntoMemory( } void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!account_id.empty()); DCHECK(!refresh_token.empty()); ValidateAccountId(account_id); - signin_metrics::LogSigninAddAccount(); - const std::string& existing_token = GetRefreshToken(account_id); if (existing_token != refresh_token) { ScopedBatchChange batch(this); @@ -751,13 +758,14 @@ void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials( } void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentialsInMemory( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!account_id.empty()); DCHECK(!refresh_token.empty()); - bool is_refresh_token_invalidated = refresh_token == kInvalidRefreshToken; + bool is_refresh_token_invalidated = + refresh_token == GaiaConstants::kInvalidRefreshToken; GoogleServiceAuthError error = is_refresh_token_invalidated ? GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( @@ -797,13 +805,13 @@ void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentialsInMemory( } void MutableProfileOAuth2TokenServiceDelegate::PersistCredentials( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token) { DCHECK(!account_id.empty()); DCHECK(!refresh_token.empty()); if (token_web_data_) { VLOG(1) << "MutablePO2TS::PersistCredentials for account_id=" << account_id; - token_web_data_->SetTokenForService(ApplyAccountIdPrefix(account_id), + token_web_data_->SetTokenForService(ApplyAccountIdPrefix(account_id.id), refresh_token); } } @@ -816,13 +824,15 @@ void MutableProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() { VLOG(1) << "MutablePO2TS::RevokeAllCredentials"; ScopedBatchChange batch(this); - if (load_credentials_state() == LOAD_CREDENTIALS_IN_PROGRESS) { + if (load_credentials_state() == + signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS) { VLOG(1) << "MutablePO2TS::RevokeAllCredentials before tokens are loaded."; // If |RevokeAllCredentials| is called while credentials are being loaded, // then the load must be cancelled and the load credentials state updated. DCHECK_NE(0, web_data_service_request_); CancelWebTokenFetch(); - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); FinishLoadingCredentials(); } @@ -841,17 +851,17 @@ void MutableProfileOAuth2TokenServiceDelegate::RevokeAllCredentials() { } void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentials( - const std::string& account_id) { + const CoreAccountId& account_id) { RevokeCredentialsImpl(account_id, /*revoke_on_server=*/true); } void MutableProfileOAuth2TokenServiceDelegate::ClearPersistedCredentials( - const std::string& account_id) { + const CoreAccountId& account_id) { DCHECK(!account_id.empty()); if (token_web_data_) { VLOG(1) << "MutablePO2TS::ClearPersistedCredentials for account_id=" << account_id; - token_web_data_->RemoveTokenForService(ApplyAccountIdPrefix(account_id)); + token_web_data_->RemoveTokenForService(ApplyAccountIdPrefix(account_id.id)); } } @@ -859,7 +869,7 @@ void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsOnServer( const std::string& refresh_token) { DCHECK(!refresh_token.empty()); - if (refresh_token == kInvalidRefreshToken) + if (refresh_token == GaiaConstants::kInvalidRefreshToken) return; // Keep track or all server revoke requests. This way they can be deleted @@ -877,8 +887,8 @@ void MutableProfileOAuth2TokenServiceDelegate::CancelWebTokenFetch() { } void MutableProfileOAuth2TokenServiceDelegate::ExtractCredentials( - OAuth2TokenService* to_service, - const std::string& account_id) { + ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) { static_cast<ProfileOAuth2TokenService*>(to_service) ->UpdateCredentials(account_id, GetRefreshToken(account_id), signin_metrics::SourceForRefreshTokenOperation:: @@ -891,7 +901,7 @@ void MutableProfileOAuth2TokenServiceDelegate::Shutdown() { server_revokes_.clear(); CancelWebTokenFetch(); refresh_tokens_.clear(); - OAuth2TokenServiceDelegate::Shutdown(); + ProfileOAuth2TokenServiceDelegate::Shutdown(); } void MutableProfileOAuth2TokenServiceDelegate::OnConnectionChanged( @@ -913,7 +923,7 @@ bool MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorIfPossible() { } void MutableProfileOAuth2TokenServiceDelegate::AddAccountStatus( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token, const GoogleServiceAuthError& error) { DCHECK_EQ(0u, refresh_tokens_.count(account_id)); @@ -926,7 +936,7 @@ void MutableProfileOAuth2TokenServiceDelegate::FinishLoadingCredentials() { } void MutableProfileOAuth2TokenServiceDelegate::RevokeCredentialsImpl( - const std::string& account_id, + const CoreAccountId& account_id, bool revoke_on_server) { ValidateAccountId(account_id); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); diff --git a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h index 906e25a5c9e..9c398812676 100644 --- a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h +++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.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_CORE_BROWSER_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ #include <memory> #include <vector> @@ -12,21 +12,21 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/threading/thread_checker.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/webdata/token_web_data.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" +#include "components/signin/public/base/account_consistency_method.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_data_service_consumer.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" #include "net/base/backoff_entry.h" #include "services/network/public/cpp/network_connection_tracker.h" class PrefRegistrySimple; class SigninClient; +class TokenWebData; class MutableProfileOAuth2TokenServiceDelegate - : public OAuth2TokenServiceDelegate, + : public ProfileOAuth2TokenServiceDelegate, public WebDataServiceConsumer, public network::NetworkConnectionTracker::NetworkConnectionObserver { public: @@ -45,44 +45,44 @@ class MutableProfileOAuth2TokenServiceDelegate static void RegisterProfilePrefs(PrefRegistrySimple* registry); - // Overridden from OAuth2TokenServiceDelegate. - OAuth2AccessTokenFetcher* CreateAccessTokenFetcher( - const std::string& account_id, + // Overridden from ProfileOAuth2TokenServiceDelegate. + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) override; // Updates the internal cache of the result from the most-recently-completed // auth request (used for reporting errors to the user). - void UpdateAuthError(const std::string& account_id, + void UpdateAuthError(const CoreAccountId& account_id, const GoogleServiceAuthError& error) override; std::string GetTokenForMultilogin( - const std::string& account_id) const override; - bool RefreshTokenIsAvailable(const std::string& account_id) const override; + const CoreAccountId& account_id) const override; + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override; GoogleServiceAuthError GetAuthError( - const std::string& account_id) const override; - std::vector<std::string> GetAccounts() override; + const CoreAccountId& account_id) const override; + std::vector<CoreAccountId> GetAccounts() const override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() const override; - void LoadCredentials(const std::string& primary_account_id) override; - void UpdateCredentials(const std::string& account_id, + void LoadCredentials(const CoreAccountId& primary_account_id) override; + void UpdateCredentials(const CoreAccountId& account_id, const std::string& refresh_token) override; void RevokeAllCredentials() override; - void RevokeCredentials(const std::string& account_id) override; - void ExtractCredentials(OAuth2TokenService* to_service, - const std::string& account_id) override; + void RevokeCredentials(const CoreAccountId& account_id) override; + void ExtractCredentials(ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) override; void Shutdown() override; // Overridden from NetworkConnectionTracker::NetworkConnectionObserver. void OnConnectionChanged(network::mojom::ConnectionType type) override; - // Overridden from OAuth2TokenServiceDelegate. + // Overridden from ProfileOAuth2TokenServiceDelegate. const net::BackoffEntry* BackoffEntry() const override; bool FixRequestErrorIfPossible() override; // Returns the account's refresh token used for testing purposes. - std::string GetRefreshTokenForTest(const std::string& account_id) const; + std::string GetRefreshTokenForTest(const CoreAccountId& account_id) const; private: friend class MutableProfileOAuth2TokenServiceDelegateTest; @@ -147,20 +147,21 @@ class MutableProfileOAuth2TokenServiceDelegate const std::map<std::string, std::string>& db_tokens); // Updates the in-memory representation of the credentials. - void UpdateCredentialsInMemory(const std::string& account_id, + void UpdateCredentialsInMemory(const CoreAccountId& account_id, const std::string& refresh_token); // Sets refresh token in error. - void InvalidateTokenForMultilogin(const std::string& failed_account) override; + void InvalidateTokenForMultilogin( + const CoreAccountId& failed_account) override; // Persists credentials for |account_id|. Enables overriding for // testing purposes, or other cases, when accessing the DB is not desired. - void PersistCredentials(const std::string& account_id, + void PersistCredentials(const CoreAccountId& account_id, const std::string& refresh_token); // Clears credentials persisted for |account_id|. Enables overriding for // testing purposes, or other cases, when accessing the DB is not desired. - void ClearPersistedCredentials(const std::string& account_id); + void ClearPersistedCredentials(const CoreAccountId& account_id); // Revokes the refresh token on the server. void RevokeCredentialsOnServer(const std::string& refresh_token); @@ -168,11 +169,11 @@ class MutableProfileOAuth2TokenServiceDelegate // Cancels any outstanding fetch for tokens from the web database. void CancelWebTokenFetch(); - std::string GetRefreshToken(const std::string& account_id) const; + std::string GetRefreshToken(const CoreAccountId& account_id) const; // Creates a new AccountStatus and adds it to the AccountStatusMap. // The account must not be already in the map. - void AddAccountStatus(const std::string& account_id, + void AddAccountStatus(const CoreAccountId& account_id, const std::string& refresh_token, const GoogleServiceAuthError& error); @@ -183,12 +184,12 @@ class MutableProfileOAuth2TokenServiceDelegate // Deletes the credential locally and notifies observers through // OnRefreshTokenRevoked(). If |revoke_on_server| is true, the token is also // revoked on the server. - void RevokeCredentialsImpl(const std::string& account_id, + void RevokeCredentialsImpl(const CoreAccountId& account_id, bool revoke_on_server); // Maps the |account_id| of accounts known to ProfileOAuth2TokenService // to information about the account. - typedef std::map<std::string, AccountStatus> AccountStatusMap; + typedef std::map<CoreAccountId, AccountStatus> AccountStatusMap; // In memory refresh token store mapping account_id to refresh_token. AccountStatusMap refresh_tokens_; @@ -197,7 +198,7 @@ class MutableProfileOAuth2TokenServiceDelegate // The primary account id of this service's profile during the loading of // credentials. This member is empty otherwise. - std::string loading_primary_account_id_; + CoreAccountId loading_primary_account_id_; std::vector<std::unique_ptr<RevokeServerRefreshToken>> server_revokes_; @@ -233,4 +234,4 @@ class MutableProfileOAuth2TokenServiceDelegate DISALLOW_COPY_AND_ASSIGN(MutableProfileOAuth2TokenServiceDelegate); }; -#endif // CHROME_BROWSER_SIGNIN_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ diff --git a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc index 7a42f2e4c1d..a25bc19bb95 100644 --- a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_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/core/browser/mutable_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h" #include <map> #include <string> @@ -20,26 +20,26 @@ #include "build/build_config.h" #include "build/buildflag.h" #include "components/os_crypt/os_crypt_mocker.h" -#include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/device_id_helper.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_buildflags.h" -#include "components/signin/core/browser/signin_manager_base.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "components/signin/core/browser/webdata/token_web_data.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/device_id_helper.h" +#include "components/signin/public/base/signin_buildflags.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/webdata/token_web_data.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_database_service.h" -#include "google_apis/gaia/fake_oauth2_token_service_delegate.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" -#include "google_apis/gaia/oauth2_token_service_test_util.h" +#include "google_apis/gaia/oauth2_access_token_fetcher.h" +#include "google_apis/gaia/oauth2_access_token_manager_test_util.h" #include "net/http/http_status_code.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/test/test_network_connection_tracker.h" @@ -81,8 +81,7 @@ AccountInfo CreateTestAccountInfo(const std::string& name, class MutableProfileOAuth2TokenServiceDelegateTest : public testing::Test, public OAuth2AccessTokenConsumer, - public OAuth2TokenService::Observer, - public OAuth2TokenService::DiagnosticsObserver, + public OAuth2TokenServiceObserver, public WebDataServiceConsumer { public: MutableProfileOAuth2TokenServiceDelegateTest() @@ -105,7 +104,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs( pref_service_.registry()); AccountTrackerService::RegisterPrefs(pref_service_.registry()); - SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); + PrimaryAccountManager::RegisterProfilePrefs(pref_service_.registry()); client_.reset(new TestSigninClient(&pref_service_)); client_->GetTestURLLoaderFactory()->AddResponse( GaiaUrls::GetInstance()->oauth2_revoke_url().spec(), ""); @@ -187,30 +186,30 @@ class MutableProfileOAuth2TokenServiceDelegateTest access_token_failure_ = error; } - // OAuth2TokenService::Observer implementation. - void OnRefreshTokenAvailable(const std::string& account_id) override { + // OAuth2TokenServiceObserver implementation. + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override { ++token_available_count_; } - void OnRefreshTokenRevoked(const std::string& account_id) override { + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override { ++token_revoked_count_; } void OnRefreshTokensLoaded() override { ++tokens_loaded_count_; } void OnEndBatchChanges() override { ++end_batch_changes_; } - void OnAuthErrorChanged(const std::string& account_id, + void OnAuthErrorChanged(const CoreAccountId& account_id, const GoogleServiceAuthError& auth_error) override { ++auth_error_changed_count_; } - // OAuth2TokenService::DiagnosticsObserver implementation + // ProfileOAuth2TokenService callbacks. void OnRefreshTokenAvailableFromSource(const CoreAccountId& account_id, bool is_refresh_token_valid, - const std::string& source) override { + const std::string& source) { source_for_refresh_token_available_ = source; } void OnRefreshTokenRevokedFromSource(const CoreAccountId& account_id, - const std::string& source) override { + const std::string& source) { source_for_refresh_token_revoked_ = source; } @@ -256,7 +255,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest std::unique_ptr<TestSigninClient> client_; std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate> oauth2_service_delegate_; - TestingOAuth2TokenServiceConsumer consumer_; + TestingOAuth2AccessTokenManagerConsumer consumer_; sync_preferences::TestingPrefServiceSyncable pref_service_; AccountTrackerService account_tracker_service_; scoped_refptr<TokenWebData> token_web_data_; @@ -276,7 +275,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceDBUpgrade) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror); - std::string main_account_id("account_id"); + CoreAccountId main_account_id("account_id"); std::string main_refresh_token("old_refresh_token"); // Populate DB with legacy tokens. @@ -344,9 +343,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceDBUpgrade) { TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceRevokeCredentials) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - std::string account_id_1 = "account_id_1"; + CoreAccountId account_id_1("account_id_1"); std::string refresh_token_1 = "refresh_token_1"; - std::string account_id_2 = "account_id_2"; + CoreAccountId account_id_2("account_id_2"); std::string refresh_token_2 = "refresh_token_2"; EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id_1)); @@ -380,30 +379,33 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, // Ensure DB is clean. oauth2_service_delegate_->RevokeAllCredentials(); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, oauth2_service_delegate_->load_credentials_state()); oauth2_service_delegate_->LoadCredentials(""); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceLoadCredentials) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror); + const CoreAccountId account_id("account_id"); + const CoreAccountId account_id2("account_id_2"); // Ensure DB is clean. oauth2_service_delegate_->RevokeAllCredentials(); ResetObserverCounts(); // Perform a load from an empty DB. - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, oauth2_service_delegate_->load_credentials_state()); oauth2_service_delegate_->LoadCredentials("account_id"); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, oauth2_service_delegate_->load_credentials_state()); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(OAuth2TokenServiceDelegate:: + EXPECT_EQ(signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT, oauth2_service_delegate_->load_credentials_state()); EXPECT_EQ(GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( @@ -427,24 +429,25 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, // is in the refresh_token map. EXPECT_EQ(1U, oauth2_service_delegate_->refresh_tokens_.size()); EXPECT_EQ( - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken, - oauth2_service_delegate_->refresh_tokens_["account_id"].refresh_token); + GaiaConstants::kInvalidRefreshToken, + oauth2_service_delegate_->refresh_tokens_[account_id].refresh_token); // Setup a DB with tokens that don't require upgrade and clear memory. - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); - oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2"); oauth2_service_delegate_->refresh_tokens_.clear(); EXPECT_EQ(2, end_batch_changes_); EXPECT_EQ(2, auth_error_changed_count_); ResetObserverCounts(); - oauth2_service_delegate_->LoadCredentials("account_id"); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + oauth2_service_delegate_->LoadCredentials(account_id); + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, oauth2_service_delegate_->load_credentials_state()); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError("account_id")); + oauth2_service_delegate_->GetAuthError(account_id)); EXPECT_EQ(2, token_available_count_); EXPECT_EQ(0, token_revoked_count_); EXPECT_EQ(1, tokens_loaded_count_); @@ -452,8 +455,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_EQ(2, auth_error_changed_count_); ResetObserverCounts(); - EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id")); - EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id2")); + EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id)); + EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id2)); oauth2_service_delegate_->RevokeAllCredentials(); EXPECT_EQ(0, token_available_count_); @@ -469,19 +472,22 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceLoadCredentialsEmptyPrimaryAccountId_DiceEnabled) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice); + const CoreAccountId account_id("account_id"); + const CoreAccountId account_id2("account_id_2"); // Ensure DB is clean. oauth2_service_delegate_->RevokeAllCredentials(); ResetObserverCounts(); // Perform a load from an empty DB. - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, oauth2_service_delegate_->load_credentials_state()); oauth2_service_delegate_->LoadCredentials(""); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, oauth2_service_delegate_->load_credentials_state()); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_EQ(1, end_batch_changes_); EXPECT_EQ(0, auth_error_changed_count_); ExpectOneTokensLoadedNotification(); @@ -491,19 +497,20 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_TRUE(oauth2_service_delegate_->refresh_tokens_.empty()); // Setup a DB with tokens that don't require upgrade and clear memory. - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); - oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2"); oauth2_service_delegate_->refresh_tokens_.clear(); EXPECT_EQ(2, end_batch_changes_); EXPECT_EQ(2, auth_error_changed_count_); ResetObserverCounts(); oauth2_service_delegate_->LoadCredentials(""); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + EXPECT_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, oauth2_service_delegate_->load_credentials_state()); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_EQ(2, token_available_count_); EXPECT_EQ(0, token_revoked_count_); EXPECT_EQ(1, tokens_loaded_count_); @@ -511,8 +518,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_EQ(2, auth_error_changed_count_); ResetObserverCounts(); - EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id")); - EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id2")); + EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id)); + EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id2)); oauth2_service_delegate_->RevokeAllCredentials(); EXPECT_EQ(0, token_available_count_); @@ -557,8 +564,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, primary_account.account_id)); EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable( secondary_account.account_id)); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_FALSE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible)); } @@ -598,8 +606,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, primary_account.account_id)); EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable( secondary_account.account_id)); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_TRUE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible)); } @@ -637,8 +646,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_EQ(1, auth_error_changed_count_); EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable( primary_account.account_id)); - EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, - oauth2_service_delegate_->load_credentials_state()); + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->load_credentials_state()); EXPECT_TRUE(pref_service_.GetBoolean(prefs::kTokenServiceDiceCompatible)); } @@ -676,14 +686,14 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable( primary_account.account_id)); EXPECT_EQ( - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken, + GaiaConstants::kInvalidRefreshToken, oauth2_service_delegate_->refresh_tokens_[primary_account.account_id] .refresh_token); EXPECT_EQ( GoogleServiceAuthError::InvalidGaiaCredentialsReason::CREDENTIALS_MISSING, oauth2_service_delegate_->GetAuthError(primary_account.account_id) .GetInvalidGaiaCredentialsReason()); - EXPECT_EQ(OAuth2TokenServiceDelegate:: + EXPECT_EQ(signin::LoadCredentialsState:: LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT, oauth2_service_delegate_->load_credentials_state()); @@ -721,16 +731,18 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, // Tests that calling UpdateCredentials revokes the old token, without sending // the notification. TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeOnUpdate) { + const CoreAccountId account_id("account_id"); + // Add a token. InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); ASSERT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); ExpectOneTokenAvailableNotification(); // Updating the token does not revoke the old one. // Regression test for http://crbug.com/865189 - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); ExpectOneTokenAvailableNotification(); @@ -739,7 +751,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeOnUpdate) { EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); // Set the same token again. - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); ExpectNoNotifications(); @@ -754,11 +766,13 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeOnUpdate) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, DelayedRevoke) { + const CoreAccountId account_id("account_id"); + client_->SetNetworkCallsDelayed(true); InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); - oauth2_service_delegate_->RevokeCredentials("account_id"); + oauth2_service_delegate_->RevokeCredentials(account_id); // The revoke does not start until network calls are unblocked. EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); @@ -772,11 +786,13 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, DelayedRevoke) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ShutdownDuringRevoke) { + const CoreAccountId account_id("account_id"); + // Shutdown cancels the revocation. client_->SetNetworkCallsDelayed(true); InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); - oauth2_service_delegate_->RevokeCredentials("account_id"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); + oauth2_service_delegate_->RevokeCredentials(account_id); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); // Shutdown. @@ -791,14 +807,15 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ShutdownDuringRevoke) { TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeRetries) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); const std::string url = GaiaUrls::GetInstance()->oauth2_revoke_url().spec(); + const CoreAccountId account_id("account_id"); // Revokes will remain in "pending" state. client_->GetTestURLLoaderFactory()->ClearResponses(); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url)); - oauth2_service_delegate_->RevokeCredentials("account_id"); + oauth2_service_delegate_->RevokeCredentials(account_id); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); // Fail and retry. @@ -818,8 +835,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeRetries) { EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); // No retry after success. - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); - oauth2_service_delegate_->RevokeCredentials("account_id"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); + oauth2_service_delegate_->RevokeCredentials(account_id); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( @@ -829,12 +846,12 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeRetries) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, UpdateInvalidToken) { + const CoreAccountId account_id("account_id"); // Add the invalid token. InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); ASSERT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); oauth2_service_delegate_->UpdateCredentials( - "account_id", - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken); + account_id, GaiaConstants::kInvalidRefreshToken); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); EXPECT_EQ(1, auth_error_changed_count_); ExpectOneTokenAvailableNotification(); @@ -844,32 +861,32 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, UpdateInvalidToken) { GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_REJECTED_BY_CLIENT)), - oauth2_service_delegate_->GetAuthError("account_id")); + oauth2_service_delegate_->GetAuthError(account_id)); // Update the token: authentication error is fixed, no actual server // revocation. - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); EXPECT_EQ(1, auth_error_changed_count_); ExpectOneTokenAvailableNotification(); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError("account_id")); + oauth2_service_delegate_->GetAuthError(account_id)); } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, InvalidateTokensForMultilogin) { - class TokenServiceErrorObserver : public OAuth2TokenService::Observer { + class TokenServiceErrorObserver : public OAuth2TokenServiceObserver { public: MOCK_METHOD2(OnAuthErrorChanged, - void(const std::string&, const GoogleServiceAuthError&)); + void(const CoreAccountId&, const GoogleServiceAuthError&)); }; InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice); TokenServiceErrorObserver observer; oauth2_service_delegate_->AddObserver(&observer); - const std::string account_id1 = "account_id1"; - const std::string account_id2 = "account_id2"; + const CoreAccountId account_id1("account_id1"); + const CoreAccountId account_id2("account_id2"); // This will be fired from UpdateCredentials. EXPECT_CALL( @@ -901,28 +918,28 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, LoadInvalidToken) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice); std::map<std::string, std::string> tokens; - tokens["AccountId-account_id"] = - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken; + const CoreAccountId account_id("account_id"); + tokens["AccountId-account_id"] = GaiaConstants::kInvalidRefreshToken; oauth2_service_delegate_->LoadAllCredentialsIntoMemory(tokens); EXPECT_EQ(1u, oauth2_service_delegate_->GetAccounts().size()); - EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable("account_id")); - EXPECT_STREQ(MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken, - oauth2_service_delegate_->GetRefreshToken("account_id").c_str()); + EXPECT_TRUE(oauth2_service_delegate_->RefreshTokenIsAvailable(account_id)); + EXPECT_STREQ(GaiaConstants::kInvalidRefreshToken, + oauth2_service_delegate_->GetRefreshToken(account_id).c_str()); // The account is in authentication error. EXPECT_EQ(GoogleServiceAuthError( GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_REJECTED_BY_CLIENT)), - oauth2_service_delegate_->GetAuthError("account_id")); + oauth2_service_delegate_->GetAuthError(account_id)); } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetTokenForMultilogin) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDice); - const std::string account_id1 = "account_id1"; - const std::string account_id2 = "account_id2"; + const CoreAccountId account_id1("account_id1"); + const CoreAccountId account_id2("account_id2"); oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token1"); oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2"); @@ -939,20 +956,22 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetTokenForMultilogin) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceNotifications) { + const CoreAccountId account_id("account_id"); + InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); ExpectOneTokenAvailableNotification(); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token"); ExpectNoNotifications(); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2"); ExpectOneTokenAvailableNotification(); - oauth2_service_delegate_->RevokeCredentials("account_id"); + oauth2_service_delegate_->RevokeCredentials(account_id); ExpectOneTokenRevokedNotification(); - oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token2"); + oauth2_service_delegate_->UpdateCredentials(account_id, "refresh_token2"); ExpectOneTokenAvailableNotification(); oauth2_service_delegate_->RevokeAllCredentials(); @@ -960,30 +979,37 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceNotifications) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetAccounts) { + const CoreAccountId account_id1("account_id1"); + const CoreAccountId account_id2("account_id2"); + InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); EXPECT_TRUE(oauth2_service_delegate_->GetAccounts().empty()); - oauth2_service_delegate_->UpdateCredentials("account_id1", "refresh_token1"); - oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2"); - std::vector<std::string> accounts = oauth2_service_delegate_->GetAccounts(); + + oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token1"); + oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2"); + std::vector<CoreAccountId> accounts = oauth2_service_delegate_->GetAccounts(); EXPECT_EQ(2u, accounts.size()); - EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1")); - EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id2")); - oauth2_service_delegate_->RevokeCredentials("account_id2"); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1)); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id2)); + oauth2_service_delegate_->RevokeCredentials(account_id2); accounts = oauth2_service_delegate_->GetAccounts(); EXPECT_EQ(1u, oauth2_service_delegate_->GetAccounts().size()); - EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1")); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1)); } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, FetchPersistentError) { + const CoreAccountId email(kEmail); + InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - oauth2_service_delegate_->UpdateCredentials(kEmail, "refreshToken"); + oauth2_service_delegate_->UpdateCredentials(email, "refreshToken"); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError(kEmail)); + oauth2_service_delegate_->GetAuthError(email)); - GoogleServiceAuthError authfail(GoogleServiceAuthError::ACCOUNT_DELETED); - oauth2_service_delegate_->UpdateAuthError(kEmail, authfail); + GoogleServiceAuthError authfail( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); + oauth2_service_delegate_->UpdateAuthError(email, authfail); EXPECT_NE(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError(kEmail)); + oauth2_service_delegate_->GetAuthError(email)); // Create a "success" fetch we don't expect to get called. AddSuccessfulOAuhTokenResponse(); @@ -992,9 +1018,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, FetchPersistentError) { EXPECT_EQ(0, access_token_failure_count_); std::vector<std::string> scope_list; scope_list.push_back("scope"); - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher = oauth2_service_delegate_->CreateAccessTokenFetcher( - kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this)); + email, oauth2_service_delegate_->GetURLLoaderFactory(), this); fetcher->Start("foo", "bar", scope_list); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0, access_token_success_count_); @@ -1002,15 +1028,17 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, FetchPersistentError) { } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RetryBackoff) { + const CoreAccountId email(kEmail); + InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); - oauth2_service_delegate_->UpdateCredentials(kEmail, "refreshToken"); + oauth2_service_delegate_->UpdateCredentials(email, "refreshToken"); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError(kEmail)); + oauth2_service_delegate_->GetAuthError(email)); GoogleServiceAuthError authfail(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - oauth2_service_delegate_->UpdateAuthError(kEmail, authfail); + oauth2_service_delegate_->UpdateAuthError(email, authfail); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), - oauth2_service_delegate_->GetAuthError(kEmail)); + oauth2_service_delegate_->GetAuthError(email)); // Create a "success" fetch we don't expect to get called just yet. AddSuccessfulOAuhTokenResponse(); @@ -1020,9 +1048,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RetryBackoff) { EXPECT_EQ(0, access_token_failure_count_); std::vector<std::string> scope_list; scope_list.push_back("scope"); - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1 = oauth2_service_delegate_->CreateAccessTokenFetcher( - kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this)); + email, oauth2_service_delegate_->GetURLLoaderFactory(), this); fetcher1->Start("foo", "bar", scope_list); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0, access_token_success_count_); @@ -1034,9 +1062,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RetryBackoff) { // Pretend that backoff has expired and try again. oauth2_service_delegate_->backoff_entry_.SetCustomReleaseTime( base::TimeTicks()); - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher2( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher2 = oauth2_service_delegate_->CreateAccessTokenFetcher( - kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this)); + email, oauth2_service_delegate_->GetURLLoaderFactory(), this); fetcher2->Start("foo", "bar", scope_list); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, access_token_success_count_); @@ -1062,9 +1090,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ResetBackoff) { EXPECT_EQ(0, access_token_failure_count_); std::vector<std::string> scope_list; scope_list.push_back("scope"); - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1 = oauth2_service_delegate_->CreateAccessTokenFetcher( - kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this)); + kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this); fetcher1->Start("foo", "bar", scope_list); base::RunLoop().RunUntilIdle(); EXPECT_EQ(0, access_token_success_count_); @@ -1073,9 +1101,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ResetBackoff) { // Notify of network change and ensure that request now runs. oauth2_service_delegate_->OnConnectionChanged( network::mojom::ConnectionType::CONNECTION_WIFI); - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher2( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher2 = oauth2_service_delegate_->CreateAccessTokenFetcher( - kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this)); + kEmail, oauth2_service_delegate_->GetURLLoaderFactory(), this); fetcher2->Start("foo", "bar", scope_list); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, access_token_success_count_); @@ -1122,14 +1150,17 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ShutdownService) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kMirror); EXPECT_TRUE(oauth2_service_delegate_->GetAccounts().empty()); - oauth2_service_delegate_->UpdateCredentials("account_id1", "refresh_token1"); - oauth2_service_delegate_->UpdateCredentials("account_id2", "refresh_token2"); - std::vector<std::string> accounts = oauth2_service_delegate_->GetAccounts(); + const CoreAccountId account_id1("account_id1"); + const CoreAccountId account_id2("account_id2"); + + oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token1"); + oauth2_service_delegate_->UpdateCredentials(account_id2, "refresh_token2"); + std::vector<CoreAccountId> accounts = oauth2_service_delegate_->GetAccounts(); EXPECT_EQ(2u, accounts.size()); - EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1")); - EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id2")); - oauth2_service_delegate_->LoadCredentials("account_id1"); - oauth2_service_delegate_->UpdateCredentials("account_id1", "refresh_token3"); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id1)); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), account_id2)); + oauth2_service_delegate_->LoadCredentials(account_id1); + oauth2_service_delegate_->UpdateCredentials(account_id1, "refresh_token3"); oauth2_service_delegate_->Shutdown(); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); EXPECT_TRUE(oauth2_service_delegate_->refresh_tokens_.empty()); @@ -1164,7 +1195,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GaiaIdMigration) { EXPECT_EQ(1, token_available_count_); EXPECT_EQ(1, end_batch_changes_); - std::vector<std::string> accounts = oauth2_service_delegate_->GetAccounts(); + std::vector<CoreAccountId> accounts = + oauth2_service_delegate_->GetAccounts(); EXPECT_EQ(1u, accounts.size()); EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(email)); @@ -1226,7 +1258,8 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, EXPECT_EQ(2, token_available_count_); EXPECT_EQ(1, end_batch_changes_); - std::vector<std::string> accounts = oauth2_service_delegate_->GetAccounts(); + std::vector<CoreAccountId> accounts = + oauth2_service_delegate_->GetAccounts(); EXPECT_EQ(2u, accounts.size()); EXPECT_FALSE(oauth2_service_delegate_->RefreshTokenIsAvailable(email1)); @@ -1304,13 +1337,13 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, // Checks that OnAuthErrorChanged() is called during UpdateCredentials(), and // that RefreshTokenIsAvailable() can be used at this time. TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, OnAuthErrorChanged) { - class TokenServiceErrorObserver : public OAuth2TokenService::Observer { + class TokenServiceErrorObserver : public OAuth2TokenServiceObserver { public: explicit TokenServiceErrorObserver( MutableProfileOAuth2TokenServiceDelegate* delegate) : delegate_(delegate) {} - void OnAuthErrorChanged(const std::string& account_id, + void OnAuthErrorChanged(const CoreAccountId& account_id, const GoogleServiceAuthError& auth_error) override { error_changed_ = true; EXPECT_EQ("account_id", account_id); @@ -1331,8 +1364,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, OnAuthErrorChanged) { // Start with the SigninErrorController in error state, so that it calls // OnErrorChanged() from AddProvider(). oauth2_service_delegate_->UpdateCredentials( - "error_account_id", - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken); + "error_account_id", GaiaConstants::kInvalidRefreshToken); TokenServiceErrorObserver token_service_observer( oauth2_service_delegate_.get()); @@ -1363,8 +1395,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetAuthError) { oauth2_service_delegate_->GetAuthError("foo")); // Add account with invalid token. oauth2_service_delegate_->UpdateCredentials( - "account_id_2", - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken); + "account_id_2", GaiaConstants::kInvalidRefreshToken); EXPECT_EQ(GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_REJECTED_BY_CLIENT), @@ -1376,13 +1407,13 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, GetAuthError) { // Regression test for https://crbug.com/824791. TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, InvalidTokenObserverCallsOrdering) { - class TokenServiceErrorObserver : public OAuth2TokenService::Observer { + class TokenServiceErrorObserver : public OAuth2TokenServiceObserver { public: explicit TokenServiceErrorObserver( MutableProfileOAuth2TokenServiceDelegate* delegate) : delegate_(delegate) {} - void OnAuthErrorChanged(const std::string& account_id, + void OnAuthErrorChanged(const CoreAccountId& account_id, const GoogleServiceAuthError& auth_error) override { error_changed_ = true; EXPECT_FALSE(token_available_) @@ -1391,14 +1422,14 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, CheckTokenState(account_id); } - void OnRefreshTokenAvailable(const std::string& account_id) override { + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override { token_available_ = true; EXPECT_TRUE(error_changed_) << "OnAuthErrorChanged() should be called first"; CheckTokenState(account_id); } - void CheckTokenState(const std::string& account_id) { + void CheckTokenState(const CoreAccountId& account_id) { EXPECT_EQ("account_id", account_id); EXPECT_TRUE(delegate_->RefreshTokenIsAvailable("account_id")); EXPECT_EQ(GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( @@ -1419,8 +1450,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, oauth2_service_delegate_.get()); oauth2_service_delegate_->AddObserver(&token_service_observer); oauth2_service_delegate_->UpdateCredentials( - "account_id", - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken); + "account_id", GaiaConstants::kInvalidRefreshToken); EXPECT_TRUE(token_service_observer.token_available_); EXPECT_TRUE(token_service_observer.error_changed_); oauth2_service_delegate_->RemoveObserver(&token_service_observer); @@ -1451,7 +1481,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ClearTokensOnStartup) { EXPECT_FALSE( oauth2_service_delegate_->RefreshTokenIsAvailable(secondary_account)); EXPECT_STREQ( - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken, + GaiaConstants::kInvalidRefreshToken, oauth2_service_delegate_->GetRefreshToken(primary_account).c_str()); EXPECT_EQ(GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: @@ -1474,7 +1504,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ClearTokensOnStartup) { EXPECT_FALSE( oauth2_service_delegate_->RefreshTokenIsAvailable(secondary_account)); EXPECT_STREQ( - MutableProfileOAuth2TokenServiceDelegate::kInvalidRefreshToken, + GaiaConstants::kInvalidRefreshToken, oauth2_service_delegate_->GetRefreshToken(primary_account).c_str()); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); } @@ -1490,7 +1520,14 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ProfileOAuth2TokenService token_service( &pref_service_, CreateOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled)); - token_service.AddDiagnosticsObserver(this); + token_service.SetRefreshTokenAvailableFromSourceCallback( + base::BindRepeating(&MutableProfileOAuth2TokenServiceDelegateTest:: + OnRefreshTokenAvailableFromSource, + base::Unretained(this))); + token_service.SetRefreshTokenRevokedFromSourceCallback( + base::BindRepeating(&MutableProfileOAuth2TokenServiceDelegateTest:: + OnRefreshTokenRevokedFromSource, + base::Unretained(this))); { base::HistogramTester h_tester; @@ -1534,9 +1571,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, "Signin.RefreshTokenUpdated.ToValidToken.Source", Source::kDiceResponseHandler_Signin, 1); - token_service.UpdateCredentials( - "account_id_2", OAuth2TokenServiceDelegate::kInvalidRefreshToken, - Source::kDiceResponseHandler_Signin); + token_service.UpdateCredentials("account_id_2", + GaiaConstants::kInvalidRefreshToken, + Source::kDiceResponseHandler_Signin); EXPECT_EQ("DiceResponseHandler::Signin", source_for_refresh_token_available_); h_tester.ExpectUniqueSample( @@ -1551,7 +1588,6 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, base::RunLoop().RunUntilIdle(); } - token_service.RemoveDiagnosticsObserver(this); token_service.Shutdown(); } @@ -1562,9 +1598,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, ExtractCredentials) { // Create another token service sync_preferences::TestingPrefServiceSyncable prefs; ProfileOAuth2TokenService::RegisterProfilePrefs(prefs.registry()); - std::unique_ptr<FakeOAuth2TokenServiceDelegate> delegate = - std::make_unique<FakeOAuth2TokenServiceDelegate>(); - FakeOAuth2TokenServiceDelegate* other_delegate = delegate.get(); + std::unique_ptr<FakeProfileOAuth2TokenServiceDelegate> delegate = + std::make_unique<FakeProfileOAuth2TokenServiceDelegate>(); + FakeProfileOAuth2TokenServiceDelegate* other_delegate = delegate.get(); ProfileOAuth2TokenService other_token_service(&prefs, std::move(delegate)); other_token_service.LoadCredentials(std::string()); diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc b/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.cc index 386c6dc93e7..d5aad257269 100644 --- a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc +++ b/chromium/components/signin/internal/identity_manager/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/core/browser/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" #include "base/android/jni_android.h" #include "base/android/jni_array.h" @@ -14,11 +14,11 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_info.h" +#include "components/signin/core/browser/android/jni_headers/OAuth2TokenService_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" #include "google_apis/gaia/oauth2_access_token_fetcher.h" -#include "jni/OAuth2TokenService_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; @@ -149,11 +149,11 @@ OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid( if (account_tracker_service_->GetMigrationState() == AccountTrackerService::MIGRATION_IN_PROGRESS) { - std::vector<std::string> accounts = GetAccounts(); + std::vector<CoreAccountId> accounts = GetAccounts(); std::vector<CoreAccountId> accounts_id; for (auto account_name : accounts) { AccountInfo account_info = - account_tracker_service_->FindAccountInfoByEmail(account_name); + account_tracker_service_->FindAccountInfoByEmail(account_name.id); DCHECK(!account_info.gaia.empty()); accounts_id.push_back(account_info.gaia); } @@ -172,7 +172,7 @@ ScopedJavaLocalRef<jobject> OAuth2TokenServiceDelegateAndroid::GetJavaObject() { } bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable( - const std::string& account_id) const { + const CoreAccountId& account_id) const { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" << " account= " << account_id; std::string account_name = MapAccountIdToAccountName(account_id); @@ -192,14 +192,14 @@ bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable( } GoogleServiceAuthError OAuth2TokenServiceDelegateAndroid::GetAuthError( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto it = errors_.find(account_id); return (it == errors_.end()) ? GoogleServiceAuthError::AuthErrorNone() : it->second; } void OAuth2TokenServiceDelegateAndroid::UpdateAuthError( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError" << " account=" << account_id << " error=" << error.ToString(); @@ -220,7 +220,8 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAuthError( FireAuthErrorChanged(account_id, error); } -std::vector<std::string> OAuth2TokenServiceDelegateAndroid::GetAccounts() { +std::vector<CoreAccountId> OAuth2TokenServiceDelegateAndroid::GetAccounts() + const { std::vector<std::string> accounts; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobjectArray> j_accounts = @@ -228,7 +229,7 @@ std::vector<std::string> OAuth2TokenServiceDelegateAndroid::GetAccounts() { // TODO(fgorski): We may decide to filter out some of the accounts. base::android::AppendJavaStringArrayToStringVector(env, j_accounts, &accounts); - return accounts; + return std::vector<CoreAccountId>(accounts.begin(), accounts.end()); } std::vector<std::string> @@ -256,7 +257,7 @@ OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { std::vector<CoreAccountId> OAuth2TokenServiceDelegateAndroid::GetValidAccounts() { std::vector<CoreAccountId> ids; - for (const std::string& id : GetAccounts()) { + for (const CoreAccountId& id : GetAccounts()) { if (ValidateAccountId(id)) ids.emplace_back(id); } @@ -272,9 +273,9 @@ void OAuth2TokenServiceDelegateAndroid::SetAccounts( Java_OAuth2TokenService_setAccounts(env, java_accounts); } -OAuth2AccessTokenFetcher* +std::unique_ptr<OAuth2AccessTokenFetcher> OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( - const std::string& account_id, + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_factory, OAuth2AccessTokenConsumer* consumer) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher" @@ -283,13 +284,13 @@ OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( std::string account_name = MapAccountIdToAccountName(account_id); DCHECK(!account_name.empty()) << "Cannot find account name for account id " << account_id; - return new AndroidAccessTokenFetcher(consumer, account_name); + return std::make_unique<AndroidAccessTokenFetcher>(consumer, account_name); } -void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken( - const std::string& account_id, +void OAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated( + const CoreAccountId& account_id, const std::string& client_id, - const OAuth2TokenService::ScopeSet& scopes, + const OAuth2AccessTokenManager::ScopeSet& scopes, const std::string& access_token) { ValidateAccountId(account_id); JNIEnv* env = AttachCurrentThread(); @@ -351,11 +352,12 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( std::vector<AccountInfo> accounts_info = account_tracker_service_->GetAccounts(); for (const AccountInfo& info : accounts_info) { - if (!base::ContainsValue(curr_ids, info.account_id)) + if (!base::Contains(curr_ids, info.account_id)) account_tracker_service_->RemoveAccount(info.account_id); } - // No need to wait for SigninManager to finish migration if not signed in. + // No need to wait for PrimaryAccountManager to finish migration if not signed + // in. if (account_tracker_service_->GetMigrationState() == AccountTrackerService::MIGRATION_IN_PROGRESS && signed_in_account_id.empty()) { @@ -376,13 +378,13 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( std::vector<CoreAccountId>* refreshed_ids, std::vector<CoreAccountId>* revoked_ids) { bool keep_accounts = base::FeatureList::IsEnabled(signin::kMiceFeature) || - base::ContainsValue(curr_ids, signed_in_id); + 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 (prev_id == signed_in_id) continue; - if (!base::ContainsValue(curr_ids, prev_id)) { + if (!base::Contains(curr_ids, prev_id)) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "revoked=" << prev_id; revoked_ids->push_back(prev_id); @@ -404,7 +406,7 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( } } else { // Revoke all ids. - if (base::ContainsValue(prev_ids, signed_in_id)) { + if (base::Contains(prev_ids, signed_in_id)) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << "revoked=" << signed_in_id; revoked_ids->push_back(signed_in_id); @@ -420,74 +422,34 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( return keep_accounts; } -void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable( - const std::string& account_id) { - DCHECK(!account_id.empty()); - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable id=" - << account_id; - std::string account_name = MapAccountIdToAccountName(account_id); - DCHECK(!account_name.empty()) - << "Cannot find account name for account id " << account_id; - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_account_name = - ConvertUTF8ToJavaString(env, account_name); - Java_OAuth2TokenService_notifyRefreshTokenAvailable(env, java_ref_, - j_account_name); - OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id); -} - -void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked( - const std::string& account_id) { - DCHECK(!account_id.empty()); - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id=" - << account_id; - std::string account_name = MapAccountIdToAccountName(account_id); - if (!account_name.empty()) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_account_name = - ConvertUTF8ToJavaString(env, account_name); - Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_, - j_account_name); - } else { - // Current prognosis is that we have an unmigrated account which is due for - // deletion. Record a histogram to debug this. - UMA_HISTOGRAM_ENUMERATION("OAuth2Login.AccountRevoked.MigrationState", - account_tracker_service_->GetMigrationState(), - AccountTrackerService::NUM_MIGRATION_STATES); - bool is_email_id = account_id.find('@') != std::string::npos; - UMA_HISTOGRAM_BOOLEAN("OAuth2Login.AccountRevoked.IsEmailId", is_email_id); - } - OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id); -} - void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded"; - - DCHECK_EQ(LOAD_CREDENTIALS_IN_PROGRESS, load_credentials_state()); - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); - - JNIEnv* env = AttachCurrentThread(); - Java_OAuth2TokenService_notifyRefreshTokensLoaded(env, java_ref_); - OAuth2TokenServiceDelegate::FireRefreshTokensLoaded(); + DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, + load_credentials_state()); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + ProfileOAuth2TokenServiceDelegate::FireRefreshTokensLoaded(); } void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials"; ScopedBatchChange batch(this); - std::vector<std::string> accounts_to_revoke = GetAccounts(); + std::vector<CoreAccountId> accounts_to_revoke = GetAccounts(); // Clear accounts in the token service before calling // |FireRefreshTokenRevoked|. SetAccounts(std::vector<CoreAccountId>()); - for (const std::string& account : accounts_to_revoke) + for (const CoreAccountId& account : accounts_to_revoke) FireRefreshTokenRevoked(account); } void OAuth2TokenServiceDelegateAndroid::LoadCredentials( - const std::string& primary_account_id) { - DCHECK_EQ(LOAD_CREDENTIALS_NOT_STARTED, load_credentials_state()); - set_load_credentials_state(LOAD_CREDENTIALS_IN_PROGRESS); + 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)) { FireRefreshTokensLoaded(); @@ -502,7 +464,7 @@ void OAuth2TokenServiceDelegateAndroid::LoadCredentials( } void OAuth2TokenServiceDelegateAndroid::ReloadAccountsFromSystem( - const std::string& primary_account_id) { + const CoreAccountId& primary_account_id) { // UpdateAccountList() effectively synchronizes the accounts in the Token // Service with those present at the system level. UpdateAccountList(primary_account_id, GetValidAccounts(), diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h b/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h index d3603245d49..2d1eedc2f8c 100644 --- a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h +++ b/chromium/components/signin/internal/identity_manager/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_CORE_BROWSER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ #include <map> #include <memory> @@ -15,20 +15,20 @@ #include "base/callback.h" #include "base/macros.h" #include "base/time/time.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.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" #include "google_apis/gaia/google_service_auth_error.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" -// A specialization of OAuth2TokenServiceDelegate that will be returned by -// OAuth2TokenServiceDelegateFactory for OS_ANDROID. This instance uses +// A specialization of ProfileOAuth2TokenServiceDelegate that will be returned +// by OAuth2TokenServiceDelegateFactory for OS_ANDROID. This instance uses // native Android features to lookup OAuth2 tokens. // -// See |OAuth2TokenServiceDelegate| for usage details. +// See |ProfileOAuth2TokenServiceDelegate| for usage details. // -// Note: requests should be started from the UI thread. To start a -// request from other thread, please use OAuth2TokenServiceRequest. -class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { +// Note: requests should be started from the UI thread. +class OAuth2TokenServiceDelegateAndroid + : public ProfileOAuth2TokenServiceDelegate { public: OAuth2TokenServiceDelegateAndroid( AccountTrackerService* account_tracker_service); @@ -47,13 +47,13 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { disable_interaction_with_system_accounts_ = true; } - // OAuth2TokenServiceDelegate overrides: - bool RefreshTokenIsAvailable(const std::string& account_id) const override; + // ProfileOAuth2TokenServiceDelegate overrides: + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override; GoogleServiceAuthError GetAuthError( - const std::string& account_id) const override; - void UpdateAuthError(const std::string& account_id, + const CoreAccountId& account_id) const override; + void UpdateAuthError(const CoreAccountId& account_id, const GoogleServiceAuthError& error) override; - std::vector<std::string> GetAccounts() override; + std::vector<CoreAccountId> GetAccounts() const override; void UpdateAccountList( JNIEnv* env, @@ -72,27 +72,25 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // OA2TService aware accounts. void RevokeAllCredentials() override; - void LoadCredentials(const std::string& primary_account_id) override; + void LoadCredentials(const CoreAccountId& primary_account_id) override; - void ReloadAccountsFromSystem(const std::string& primary_account_id) override; + void ReloadAccountsFromSystem( + const CoreAccountId& primary_account_id) override; protected: - OAuth2AccessTokenFetcher* CreateAccessTokenFetcher( - const std::string& account_id, + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_factory, OAuth2AccessTokenConsumer* consumer) override; - // Overridden from OAuth2TokenService to intercept token fetch requests and - // redirect them to the Account Manager. - void InvalidateAccessToken(const std::string& account_id, - const std::string& client_id, - const OAuth2TokenService::ScopeSet& scopes, - const std::string& access_token) override; - - // Called to notify observers when a refresh token is available. - void FireRefreshTokenAvailable(const std::string& account_id) override; - // Called to notify observers when a refresh token has been revoked. - void FireRefreshTokenRevoked(const std::string& account_id) override; + // Overridden from ProfileOAuth2TokenServiceDelegate to intercept token fetch + // requests and redirect them to the Account Manager. + void OnAccessTokenInvalidated( + const CoreAccountId& account_id, + const std::string& client_id, + const OAuth2AccessTokenManager::ScopeSet& scopes, + const std::string& access_token) override; + // Called to notify observers when refresh tokans have been loaded. void FireRefreshTokensLoaded() override; @@ -128,7 +126,7 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { base::android::ScopedJavaGlobalRef<jobject> java_ref_; // Maps account_id to the last error for that account. - std::map<std::string, GoogleServiceAuthError> errors_; + std::map<CoreAccountId, GoogleServiceAuthError> errors_; AccountTrackerService* account_tracker_service_; RefreshTokenLoadStatus fire_refresh_token_loaded_; @@ -139,4 +137,4 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceDelegateAndroid); }; -#endif // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH2_TOKEN_SERVICE_DELEGATE_ANDROID_H_ diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc b/chromium/components/signin/internal/identity_manager/oauth2_token_service_delegate_android_unittest.cc index 6fe207ad174..731a71c9955 100644 --- a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/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/core/browser/oauth2_token_service_delegate_android.h" +#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gmock/include/gmock/gmock.h" @@ -27,10 +27,10 @@ class OAuth2TokenServiceDelegateAndroidForTest MOCK_METHOD1(SetAccounts, void(const std::vector<CoreAccountId>&)); }; -class TestObserver : public OAuth2TokenService::Observer { +class TestObserver : public OAuth2TokenServiceObserver { public: - MOCK_METHOD1(OnRefreshTokenAvailable, void(const std::string&)); - MOCK_METHOD1(OnRefreshTokenRevoked, void(const std::string&)); + MOCK_METHOD1(OnRefreshTokenAvailable, void(const CoreAccountId&)); + MOCK_METHOD1(OnRefreshTokenRevoked, void(const CoreAccountId&)); MOCK_METHOD0(OnRefreshTokensLoaded, void()); }; } // namespace @@ -120,7 +120,7 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, .InSequence(seq) .WillOnce(Return()); // Stored account from |GetAccounts| must fire a revoked event - EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -137,7 +137,7 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -154,7 +154,7 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -172,10 +172,10 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, .InSequence(seq) .WillOnce(Return()); // Previously stored account is removed, new account is available - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -191,7 +191,7 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -218,7 +218,7 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -236,10 +236,10 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, .InSequence(seq) .WillOnce(Return()); // OnRefreshTokenAvailable fired, signed in account should go first. - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); @@ -257,10 +257,10 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, .InSequence(seq) .WillOnce(Return()); // OnRefreshTokenAvailable fired, signed in account should go first. - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id)) .InSequence(seq) .WillOnce(Return()); delegate_->UpdateAccountList(account1_.account_id, {account2_.account_id}, @@ -277,10 +277,10 @@ TEST_F(OAuth2TokenServiceDelegateAndroidTest, .InSequence(seq) .WillOnce(Return()); // OnRefreshTokenAvailable fired, signed in account should go first. - EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id)) .InSequence(seq) .WillOnce(Return()); - EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id)) + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id)) .InSequence(seq) .WillOnce(Return()); diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.cc index ab38dc09254..e5a314674d9 100644 --- a/chromium/components/signin/core/browser/oauth_multilogin_helper.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.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/core/browser/oauth_multilogin_helper.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_helper.h" #include <algorithm> #include <utility> @@ -12,9 +12,9 @@ #include "base/callback.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h" -#include "components/signin/core/browser/set_accounts_in_cookie_result.h" -#include "components/signin/core/browser/signin_client.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.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" #include "google_apis/gaia/oauth_multilogin_result.h" #include "mojo/public/cpp/bindings/callback_helpers.h" @@ -25,41 +25,51 @@ namespace { constexpr int kMaxFetcherRetries = 3; std::string FindTokenForAccount( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs, - const std::string& account_id) { - for (auto it = token_id_pairs.cbegin(); it != token_id_pairs.cend(); ++it) { - if (account_id == it->gaia_id_) - return it->token_; + const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& + gaia_id_token_pairs, + const std::string& gaia_id) { + for (const auto& gaia_id_token : gaia_id_token_pairs) { + if (gaia_id == gaia_id_token.gaia_id_) + return gaia_id_token.token_; } return std::string(); } +CoreAccountId FindAccountIdForGaiaId( + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts, + const std::string& gaia_id) { + for (const auto& account : accounts) { + if (gaia_id == account.second) + return account.first; + } + return CoreAccountId(); +} + } // namespace namespace signin { OAuthMultiloginHelper::OAuthMultiloginHelper( SigninClient* signin_client, - OAuth2TokenService* token_service, - const std::vector<std::string>& account_ids, + ProfileOAuth2TokenService* token_service, + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts, const std::string& external_cc_result, - base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback) + base::OnceCallback<void(SetAccountsInCookieResult)> callback) : signin_client_(signin_client), token_service_(token_service), - account_ids_(account_ids), + accounts_(accounts), external_cc_result_(external_cc_result), - callback_(std::move(callback)), - weak_ptr_factory_(this) { + callback_(std::move(callback)) { DCHECK(signin_client_); DCHECK(token_service_); - DCHECK(!account_ids_.empty()); + DCHECK(!accounts_.empty()); DCHECK(callback_); #ifndef NDEBUG // Check that there is no duplicate accounts. - std::set<std::string> accounts_no_duplicates(account_ids_.begin(), - account_ids_.end()); - DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size()); + std::set<GaiaCookieManagerService::AccountIdGaiaIdPair> + accounts_no_duplicates(accounts_.begin(), accounts_.end()); + DCHECK_EQ(accounts_.size(), accounts_no_duplicates.size()); #endif StartFetchingTokens(); @@ -69,9 +79,13 @@ OAuthMultiloginHelper::~OAuthMultiloginHelper() = default; void OAuthMultiloginHelper::StartFetchingTokens() { DCHECK(!token_fetcher_); - DCHECK(token_id_pairs_.empty()); - token_fetcher_ = std::make_unique<signin::OAuthMultiloginTokenFetcher>( - signin_client_, token_service_, account_ids_, + DCHECK(gaia_id_token_pairs_.empty()); + std::vector<CoreAccountId> account_ids; + for (const auto& account : accounts_) + account_ids.push_back(account.first); + + token_fetcher_ = std::make_unique<OAuthMultiloginTokenFetcher>( + signin_client_, token_service_, account_ids, base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensSuccess, base::Unretained(this)), base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensFailure, @@ -79,10 +93,17 @@ void OAuthMultiloginHelper::StartFetchingTokens() { } void OAuthMultiloginHelper::OnAccessTokensSuccess( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs) { - DCHECK(token_id_pairs_.empty()); - token_id_pairs_ = token_id_pairs; - DCHECK_EQ(token_id_pairs_.size(), account_ids_.size()); + const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>& + account_token_pairs) { + DCHECK(gaia_id_token_pairs_.empty()); + for (size_t index = 0; index < accounts_.size(); index++) { + // OAuthMultiloginTokenFetcher should return the tokens in the same order + // as the account_ids that was passed to it. + DCHECK_EQ(accounts_[index].first, account_token_pairs[index].account_id); + gaia_id_token_pairs_.emplace_back(accounts_[index].second, + account_token_pairs[index].token); + } + DCHECK_EQ(gaia_id_token_pairs_.size(), accounts_.size()); token_fetcher_.reset(); signin_client_->DelayNetworkCall( @@ -93,43 +114,45 @@ void OAuthMultiloginHelper::OnAccessTokensSuccess( void OAuthMultiloginHelper::OnAccessTokensFailure( const GoogleServiceAuthError& error) { token_fetcher_.reset(); - std::move(callback_).Run( - error.IsTransientError() - ? signin::SetAccountsInCookieResult::kTransientError - : signin::SetAccountsInCookieResult::kPersistentError); + std::move(callback_).Run(error.IsTransientError() + ? SetAccountsInCookieResult::kTransientError + : SetAccountsInCookieResult::kPersistentError); // Do not add anything below this line, because this may be deleted. } void OAuthMultiloginHelper::StartFetchingMultiLogin() { - DCHECK_EQ(token_id_pairs_.size(), account_ids_.size()); + DCHECK_EQ(gaia_id_token_pairs_.size(), accounts_.size()); gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome); - gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_, + gaia_auth_fetcher_->StartOAuthMultilogin(gaia_id_token_pairs_, external_cc_result_); } void OAuthMultiloginHelper::OnOAuthMultiloginFinished( const OAuthMultiloginResult& result) { if (result.status() == OAuthMultiloginResponseStatus::kOk) { + std::vector<std::string> account_ids; + for (const auto& account : accounts_) + account_ids.push_back(account.first.id); VLOG(1) << "Multilogin successful accounts=" - << base::JoinString(account_ids_, " "); + << base::JoinString(account_ids, " "); StartSettingCookies(result); return; } // If Gaia responded with kInvalidTokens, we have to mark tokens as invalid. if (result.status() == OAuthMultiloginResponseStatus::kInvalidTokens) { - for (const std::string& failed_account_id : result.failed_accounts()) { + for (const std::string& failed_gaia_id : result.failed_gaia_ids()) { std::string failed_token = - FindTokenForAccount(token_id_pairs_, failed_account_id); + FindTokenForAccount(gaia_id_token_pairs_, failed_gaia_id); if (failed_token.empty()) { LOG(ERROR) << "Unexpected failed token for account not present in request: " - << failed_account_id; + << failed_gaia_id; continue; } - token_service_->InvalidateTokenForMultilogin(failed_account_id, - failed_token); + token_service_->InvalidateTokenForMultilogin( + FindAccountIdForGaiaId(accounts_, failed_gaia_id), failed_token); } } @@ -138,13 +161,13 @@ void OAuthMultiloginHelper::OnOAuthMultiloginFinished( result.status() == OAuthMultiloginResponseStatus::kRetry; if (is_transient_error && ++fetcher_retries_ < kMaxFetcherRetries) { - token_id_pairs_.clear(); + gaia_id_token_pairs_.clear(); StartFetchingTokens(); return; } - std::move(callback_).Run( - is_transient_error ? signin::SetAccountsInCookieResult::kTransientError - : signin::SetAccountsInCookieResult::kPersistentError); + std::move(callback_).Run(is_transient_error + ? SetAccountsInCookieResult::kTransientError + : SetAccountsInCookieResult::kPersistentError); // Do not add anything below this line, because this may be deleted. } @@ -195,7 +218,7 @@ void OAuthMultiloginHelper::OnCookieSet( } UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success); if (cookies_to_set_.empty()) - std::move(callback_).Run(signin::SetAccountsInCookieResult::kSuccess); + std::move(callback_).Run(SetAccountsInCookieResult::kSuccess); // Do not add anything below this line, because this may be deleted. } diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper.h b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h index 69064a9f0b0..e91721eaff8 100644 --- a/chromium/components/signin/core/browser/oauth_multilogin_helper.h +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.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_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_HELPER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_HELPER_H_ #include <memory> #include <set> @@ -13,18 +13,20 @@ #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 "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "services/network/public/mojom/cookie_manager.mojom.h" class GaiaAuthFetcher; class GoogleServiceAuthError; -class OAuth2TokenService; +class ProfileOAuth2TokenService; class SigninClient; namespace signin { -class OAuthMultiloginTokenFetcher; enum class SetAccountsInCookieResult; // This is a helper class that drives the OAuth multilogin process. @@ -37,10 +39,11 @@ class OAuthMultiloginHelper : public GaiaAuthConsumer { public: OAuthMultiloginHelper( SigninClient* signin_client, - OAuth2TokenService* token_service, - const std::vector<std::string>& account_ids, + ProfileOAuth2TokenService* token_service, + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& + accounts, const std::string& external_cc_result, - base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback); + base::OnceCallback<void(SetAccountsInCookieResult)> callback); ~OAuthMultiloginHelper() override; @@ -50,8 +53,8 @@ class OAuthMultiloginHelper : public GaiaAuthConsumer { // Callbacks for OAuthMultiloginTokenFetcher. void OnAccessTokensSuccess( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& - token_id_pairs); + const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>& + account_token_pairs); void OnAccessTokensFailure(const GoogleServiceAuthError& error); // Actual call to the multilogin endpoint. @@ -69,18 +72,18 @@ class OAuthMultiloginHelper : public GaiaAuthConsumer { net::CanonicalCookie::CookieInclusionStatus status); SigninClient* signin_client_; - OAuth2TokenService* token_service_; + ProfileOAuth2TokenService* token_service_; int fetcher_retries_ = 0; // Account ids to set in the cookie. - const std::vector<std::string> account_ids_; + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> accounts_; // See GaiaCookieManagerService::ExternalCcResultFetcher for details. const std::string external_cc_result_; // Access tokens, in the same order as the account ids. - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_; + std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> gaia_id_token_pairs_; - base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback_; + base::OnceCallback<void(SetAccountsInCookieResult)> callback_; std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; std::unique_ptr<OAuthMultiloginTokenFetcher> token_fetcher_; @@ -88,11 +91,11 @@ class OAuthMultiloginHelper : public GaiaAuthConsumer { // cookie jar. std::set<std::pair<std::string, std::string>> cookies_to_set_; - base::WeakPtrFactory<OAuthMultiloginHelper> weak_ptr_factory_; + base::WeakPtrFactory<OAuthMultiloginHelper> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginHelper); }; } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_HELPER_H_ diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc index 8c19a4b7009..9ad5f88d61e 100644 --- a/chromium/components/signin/core/browser/oauth_multilogin_helper_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper_unittest.cc @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/core/browser/oauth_multilogin_helper.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_helper.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/test/scoped_task_environment.h" -#include "components/signin/core/browser/set_accounts_in_cookie_result.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "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/set_accounts_in_cookie_result.h" #include "google_apis/gaia/gaia_urls.h" #include "services/network/test/test_cookie_manager.h" #include "services/network/test/test_url_loader_factory.h" @@ -21,8 +22,10 @@ namespace signin { namespace { -const char kAccountId[] = "account_id_1"; -const char kAccountId2[] = "account_id_2"; +const CoreAccountId kAccountId("account_id_1"); +const CoreAccountId kAccountId2("account_id_2"); +const char kGaiaId[] = "gaia_id_1"; +const char kGaiaId2[] = "gaia_id_2"; const char kAccessToken[] = "access_token_1"; const char kAccessToken2[] = "access_token_2"; @@ -119,8 +122,8 @@ const char kMultiloginInvalidTokenResponse[] = { "status": "INVALID_TOKENS", "failed_accounts": [ - { "obfuscated_id": "account_id_1", "status": "RECOVERABLE" }, - { "obfuscated_id": "account_id_2", "status": "OK" } + { "obfuscated_id": "gaia_id_1", "status": "RECOVERABLE" }, + { "obfuscated_id": "gaia_id_2", "status": "OK" } ] } )"; @@ -149,17 +152,21 @@ class MockCookieManager SetCanonicalCookieCallback callback)); }; -class MockTokenService : public FakeOAuth2TokenService { +class MockTokenService : public FakeProfileOAuth2TokenService { public: + MockTokenService(PrefService* prefs) : FakeProfileOAuth2TokenService(prefs) {} + MOCK_METHOD2(InvalidateTokenForMultilogin, - void(const std::string& account_id, const std::string& token)); + void(const CoreAccountId& account_id, const std::string& token)); }; } // namespace class OAuthMultiloginHelperTest : public testing::Test { public: - OAuthMultiloginHelperTest() : test_signin_client_(/*prefs=*/nullptr) { + 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(); @@ -169,17 +176,19 @@ class OAuthMultiloginHelperTest : public testing::Test { ~OAuthMultiloginHelperTest() override = default; std::unique_ptr<OAuthMultiloginHelper> CreateHelper( - const std::vector<std::string> account_ids) { + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> + accounts) { return std::make_unique<OAuthMultiloginHelper>( - &test_signin_client_, token_service(), account_ids, std::string(), + &test_signin_client_, token_service(), accounts, std::string(), base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, base::Unretained(this))); } std::unique_ptr<OAuthMultiloginHelper> CreateHelperWithExternalCcResult( - const std::vector<std::string> account_ids) { + const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> + accounts) { return std::make_unique<OAuthMultiloginHelper>( - &test_signin_client_, token_service(), account_ids, kExternalCcResult, + &test_signin_client_, token_service(), accounts, kExternalCcResult, base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, base::Unretained(this))); } @@ -202,7 +211,7 @@ class OAuthMultiloginHelperTest : public testing::Test { MockTokenService* token_service() { return &mock_token_service_; } protected: - void OnOAuthMultiloginFinished(signin::SetAccountsInCookieResult result) { + void OnOAuthMultiloginFinished(SetAccountsInCookieResult result) { DCHECK(!callback_called_); callback_called_ = true; result_ = result; @@ -211,8 +220,9 @@ class OAuthMultiloginHelperTest : public testing::Test { base::test::ScopedTaskEnvironment scoped_task_environment_; bool callback_called_ = false; - signin::SetAccountsInCookieResult result_; + SetAccountsInCookieResult result_; + TestingPrefServiceSimple pref_service_; MockCookieManager* mock_cookie_manager_; // Owned by test_signin_client_ TestSigninClient test_signin_client_; MockTokenService mock_token_service_; @@ -220,8 +230,9 @@ class OAuthMultiloginHelperTest : public testing::Test { // Everything succeeds. TEST_F(OAuthMultiloginHelperTest, Success) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); // Configure mock cookie manager: // - check that the cookie is the expected one @@ -243,13 +254,14 @@ TEST_F(OAuthMultiloginHelperTest, Success) { url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); + EXPECT_EQ(SetAccountsInCookieResult::kSuccess, result_); } // Multiple cookies in the multilogin response. TEST_F(OAuthMultiloginHelperTest, MultipleCookies) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); // Configure mock cookie manager: // - check that the cookie is the expected one @@ -277,14 +289,14 @@ TEST_F(OAuthMultiloginHelperTest, MultipleCookies) { kMultiloginSuccessResponseTwoCookies); EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); + EXPECT_EQ(SetAccountsInCookieResult::kSuccess, result_); } // Multiple cookies in the multilogin response. TEST_F(OAuthMultiloginHelperTest, SuccessWithExternalCcResult) { - token_service()->AddAccount(kAccountId); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); std::unique_ptr<OAuthMultiloginHelper> helper = - CreateHelperWithExternalCcResult({kAccountId}); + CreateHelperWithExternalCcResult({{kAccountId, kGaiaId}}); // Configure mock cookie manager: // - check that the cookie is the expected one @@ -314,25 +326,27 @@ TEST_F(OAuthMultiloginHelperTest, SuccessWithExternalCcResult) { EXPECT_FALSE( url_loader()->IsPending(multilogin_url_with_external_cc_result())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); + EXPECT_EQ(SetAccountsInCookieResult::kSuccess, result_); } // Failure to get the access token. TEST_F(OAuthMultiloginHelperTest, OneAccountAccessTokenFailure) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); token_service()->IssueErrorForAllPendingRequestsForAccount( kAccountId, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kPersistentError, result_); + EXPECT_EQ(SetAccountsInCookieResult::kPersistentError, result_); } // Retry on transient errors in the multilogin call. TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); // Configure mock cookie manager: // - check that the cookie is the expected one @@ -361,14 +375,15 @@ TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) { url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); + EXPECT_EQ(SetAccountsInCookieResult::kSuccess, result_); } // Stop retrying after too many transient errors in the multilogin call. TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginErrorMaxRetries) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); // Issue access token. OAuth2AccessTokenConsumer::TokenResponse success_response; @@ -385,13 +400,14 @@ TEST_F(OAuthMultiloginHelperTest, // Failure after exceeding the maximum number of retries. EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kTransientError, result_); + EXPECT_EQ(SetAccountsInCookieResult::kTransientError, result_); } // Persistent error in the multilogin call. TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) { - token_service()->AddAccount(kAccountId); - std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({{kAccountId, kGaiaId}}); // Issue access token. OAuth2AccessTokenConsumer::TokenResponse success_response; @@ -404,15 +420,15 @@ TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) { url_loader()->AddResponse(multilogin_url(), "blah"); // Unexpected response. EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kPersistentError, result_); + EXPECT_EQ(SetAccountsInCookieResult::kPersistentError, result_); } // Retry on "invalid token" in the multilogin response. TEST_F(OAuthMultiloginHelperTest, InvalidTokenError) { - token_service()->AddAccount(kAccountId); - token_service()->AddAccount(kAccountId2); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + token_service()->UpdateCredentials(kAccountId2, "refresh_token"); std::unique_ptr<OAuthMultiloginHelper> helper = - CreateHelper({kAccountId, kAccountId2}); + CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}}); // Configure mock cookie manager: // - check that the cookie is the expected one @@ -452,15 +468,15 @@ TEST_F(OAuthMultiloginHelperTest, InvalidTokenError) { url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); + EXPECT_EQ(SetAccountsInCookieResult::kSuccess, result_); } // Retry on "invalid token" in the multilogin response. TEST_F(OAuthMultiloginHelperTest, InvalidTokenErrorMaxRetries) { - token_service()->AddAccount(kAccountId); - token_service()->AddAccount(kAccountId2); + token_service()->UpdateCredentials(kAccountId, "refresh_token"); + token_service()->UpdateCredentials(kAccountId2, "refresh_token"); std::unique_ptr<OAuthMultiloginHelper> helper = - CreateHelper({kAccountId, kAccountId2}); + CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}}); // The failed access token should be invalidated. EXPECT_CALL(*token_service(), @@ -489,7 +505,7 @@ TEST_F(OAuthMultiloginHelperTest, InvalidTokenErrorMaxRetries) { // The maximum number of retries is reached, fail. EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); EXPECT_TRUE(callback_called_); - EXPECT_EQ(signin::SetAccountsInCookieResult::kTransientError, result_); + EXPECT_EQ(SetAccountsInCookieResult::kTransientError, result_); } } // namespace signin diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.cc index d01a50aa468..7ee1cecab12 100644 --- a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.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/core/browser/oauth_multilogin_token_fetcher.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h" #include <algorithm> #include <set> @@ -12,7 +12,8 @@ #include "base/callback.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "components/signin/core/browser/signin_client.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/signin_client.h" namespace { @@ -27,17 +28,16 @@ namespace signin { OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher( SigninClient* signin_client, - OAuth2TokenService* token_service, - const std::vector<std::string>& account_ids, + ProfileOAuth2TokenService* token_service, + const std::vector<CoreAccountId>& account_ids, SuccessCallback success_callback, FailureCallback failure_callback) - : OAuth2TokenService::Consumer("oauth_multilogin_token_fetcher"), + : OAuth2AccessTokenManager::Consumer("oauth_multilogin_token_fetcher"), signin_client_(signin_client), token_service_(token_service), account_ids_(account_ids), success_callback_(std::move(success_callback)), - failure_callback_(std::move(failure_callback)), - weak_ptr_factory_(this) { + failure_callback_(std::move(failure_callback)) { DCHECK(signin_client_); DCHECK(token_service_); DCHECK(!account_ids_.empty()); @@ -46,28 +46,28 @@ OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher( #ifndef NDEBUG // Check that there is no duplicate accounts. - std::set<std::string> accounts_no_duplicates(account_ids_.begin(), - account_ids_.end()); + std::set<CoreAccountId> accounts_no_duplicates(account_ids_.begin(), + account_ids_.end()); DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size()); #endif - for (const std::string& account_id : account_ids_) + for (const CoreAccountId& account_id : account_ids_) StartFetchingToken(account_id); } OAuthMultiloginTokenFetcher::~OAuthMultiloginTokenFetcher() = default; void OAuthMultiloginTokenFetcher::StartFetchingToken( - const std::string& account_id) { + const CoreAccountId& account_id) { DCHECK(!account_id.empty()); token_requests_.push_back( token_service_->StartRequestForMultilogin(account_id, this)); } void OAuthMultiloginTokenFetcher::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - std::string account_id = request->GetAccountId(); + CoreAccountId account_id = request->GetAccountId(); DCHECK(account_ids_.cend() != std::find(account_ids_.cbegin(), account_ids_.cend(), account_id)); @@ -82,24 +82,22 @@ void OAuthMultiloginTokenFetcher::OnGetTokenSuccess( DCHECK(inserted.second); // If this fires, we have a duplicate account. if (access_tokens_.size() == account_ids_.size()) { - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs; + std::vector<AccountIdTokenPair> account_token_pairs; for (const auto& id : account_ids_) { const auto& it = access_tokens_.find(id); DCHECK(!it->second.empty()); - // TODO(https://crbug.com/956503): Don't assume that the account ID is the - // Gaia ID. - token_id_pairs.emplace_back(id, it->second); + account_token_pairs.emplace_back(id, it->second); } RecordGetAccessTokenFinished(GoogleServiceAuthError::AuthErrorNone()); - std::move(success_callback_).Run(token_id_pairs); + std::move(success_callback_).Run(account_token_pairs); // Do not add anything below this line, as this may be deleted. } } void OAuthMultiloginTokenFetcher::OnGetTokenFailure( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) { - std::string account_id = request->GetAccountId(); + CoreAccountId account_id = request->GetAccountId(); VLOG(1) << "Failed to retrieve accesstoken account=" << account_id << " error=" << error.ToString(); if (error.IsTransientError() && @@ -123,7 +121,7 @@ void OAuthMultiloginTokenFetcher::OnGetTokenFailure( } void OAuthMultiloginTokenFetcher::EraseRequest( - const OAuth2TokenService::Request* request) { + const OAuth2AccessTokenManager::Request* request) { for (auto it = token_requests_.begin(); it != token_requests_.end(); ++it) { if (it->get() == request) { token_requests_.erase(it); diff --git a/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h new file mode 100644 index 00000000000..8f5a5d62e8d --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h @@ -0,0 +1,84 @@ +// 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_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ + +#include <map> +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/bind_helpers.h" +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" + +class SigninClient; +class ProfileOAuth2TokenService; + +namespace signin { + +// Fetches multilogin access tokens in parallel for multiple accounts. +// It is safe to delete this object from within the callbacks. +class OAuthMultiloginTokenFetcher : public OAuth2AccessTokenManager::Consumer { + public: + struct AccountIdTokenPair { + CoreAccountId account_id; + std::string token; + + AccountIdTokenPair(const CoreAccountId& account_id, + const std::string& token) + : account_id(account_id), token(token) {} + }; + using SuccessCallback = + base::OnceCallback<void(const std::vector<AccountIdTokenPair>&)>; + using FailureCallback = + base::OnceCallback<void(const GoogleServiceAuthError&)>; + + OAuthMultiloginTokenFetcher(SigninClient* signin_client, + ProfileOAuth2TokenService* token_service, + const std::vector<CoreAccountId>& account_ids, + SuccessCallback success_callback, + FailureCallback failure_callback); + + ~OAuthMultiloginTokenFetcher() override; + + private: + void StartFetchingToken(const CoreAccountId& account_id); + + // Overridden from OAuth2AccessTokenManager::Consumer. + void OnGetTokenSuccess( + const OAuth2AccessTokenManager::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; + void OnGetTokenFailure(const OAuth2AccessTokenManager::Request* request, + const GoogleServiceAuthError& error) override; + + // Helper function to remove a request from token_requests_. + void EraseRequest(const OAuth2AccessTokenManager::Request* request); + + SigninClient* signin_client_; + ProfileOAuth2TokenService* token_service_; + const std::vector<CoreAccountId> account_ids_; + + SuccessCallback success_callback_; + FailureCallback failure_callback_; + + std::vector<std::unique_ptr<OAuth2AccessTokenManager::Request>> + token_requests_; + std::map<CoreAccountId, std::string> access_tokens_; + std::set<CoreAccountId> retried_requests_; // Requests are retried once. + + base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginTokenFetcher); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher_unittest.cc index a40df52a3d6..ebeb12b0a3a 100644 --- a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_token_fetcher_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/core/browser/oauth_multilogin_token_fetcher.h" +#include "components/signin/internal/identity_manager/oauth_multilogin_token_fetcher.h" #include <map> #include <memory> @@ -11,8 +11,9 @@ #include "base/bind_helpers.h" #include "base/callback.h" #include "base/test/scoped_task_environment.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "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 "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,7 +22,7 @@ namespace signin { namespace { -const char kAccountId[] = "account_id"; +const CoreAccountId kAccountId("account_id"); const char kAccessToken[] = "access_token"; // Status of the token fetch. @@ -31,12 +32,13 @@ enum class FetchStatus { kSuccess, kFailure, kPending }; class OAuthMultiloginTokenFetcherTest : public testing::Test { public: - OAuthMultiloginTokenFetcherTest() : test_signin_client_(/*prefs=*/nullptr) {} + OAuthMultiloginTokenFetcherTest() + : test_signin_client_(&pref_service_), token_service_(&pref_service_) {} ~OAuthMultiloginTokenFetcherTest() override = default; std::unique_ptr<OAuthMultiloginTokenFetcher> CreateFetcher( - const std::vector<std::string> account_ids) { + const std::vector<CoreAccountId> account_ids) { return std::make_unique<OAuthMultiloginTokenFetcher>( &test_signin_client_, &token_service_, account_ids, base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnSuccess, @@ -55,12 +57,13 @@ class OAuthMultiloginTokenFetcherTest : public testing::Test { protected: // Success callback for OAuthMultiloginTokenFetcher. - void OnSuccess(const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& - token_id_pairs) { + void OnSuccess( + const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>& + account_id_token_pairs) { DCHECK(!success_callback_called_); - DCHECK(token_id_pairs_.empty()); + DCHECK(account_id_token_pairs_.empty()); success_callback_called_ = true; - token_id_pairs_ = token_id_pairs; + account_id_token_pairs_ = account_id_token_pairs; } // Failure callback for OAuthMultiloginTokenFetcher. @@ -75,14 +78,16 @@ class OAuthMultiloginTokenFetcherTest : public testing::Test { bool success_callback_called_ = false; bool failure_callback_called_ = false; GoogleServiceAuthError error_; - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_; + std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair> + account_id_token_pairs_; + TestingPrefServiceSimple pref_service_; TestSigninClient test_signin_client_; - FakeOAuth2TokenService token_service_; + FakeProfileOAuth2TokenService token_service_; }; TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) { - token_service_.AddAccount(kAccountId); + token_service_.UpdateCredentials(kAccountId, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = CreateFetcher({kAccountId}); EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); @@ -91,13 +96,13 @@ TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) { token_service_.IssueAllTokensForAccount(kAccountId, success_response); EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); // Check result. - EXPECT_EQ(1u, token_id_pairs_.size()); - EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_); - EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_); + EXPECT_EQ(1u, account_id_token_pairs_.size()); + EXPECT_EQ(kAccountId, account_id_token_pairs_[0].account_id); + EXPECT_EQ(kAccessToken, account_id_token_pairs_[0].token); } TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) { - token_service_.AddAccount(kAccountId); + token_service_.UpdateCredentials(kAccountId, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = CreateFetcher({kAccountId}); EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); @@ -109,7 +114,7 @@ TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) { } TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) { - token_service_.AddAccount(kAccountId); + token_service_.UpdateCredentials(kAccountId, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = CreateFetcher({kAccountId}); // Connection failure will be retried. @@ -123,13 +128,13 @@ TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) { token_service_.IssueAllTokensForAccount(kAccountId, success_response); EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); // Check result. - EXPECT_EQ(1u, token_id_pairs_.size()); - EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_); - EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_); + EXPECT_EQ(1u, account_id_token_pairs_.size()); + EXPECT_EQ(kAccountId, account_id_token_pairs_[0].account_id); + EXPECT_EQ(kAccessToken, account_id_token_pairs_[0].token); } TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) { - token_service_.AddAccount(kAccountId); + token_service_.UpdateCredentials(kAccountId, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = CreateFetcher({kAccountId}); // Repeated connection failures. @@ -147,76 +152,85 @@ TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) { // The flow succeeds even if requests are received out of order. TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsSuccess) { - token_service_.AddAccount("account_1"); - token_service_.AddAccount("account_2"); - token_service_.AddAccount("account_3"); + const CoreAccountId account_1("account_1"); + const CoreAccountId account_2("account_2"); + const CoreAccountId account_3("account_3"); + token_service_.UpdateCredentials(account_1, "refresh_token"); + token_service_.UpdateCredentials(account_2, "refresh_token"); + token_service_.UpdateCredentials(account_3, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = - CreateFetcher({"account_1", "account_2", "account_3"}); + CreateFetcher({account_1, account_2, account_3}); OAuth2AccessTokenConsumer::TokenResponse success_response; success_response.access_token = "token_3"; - token_service_.IssueAllTokensForAccount("account_3", success_response); + token_service_.IssueAllTokensForAccount(account_3, success_response); success_response.access_token = "token_1"; - token_service_.IssueAllTokensForAccount("account_1", success_response); + token_service_.IssueAllTokensForAccount(account_1, success_response); EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); success_response.access_token = "token_2"; - token_service_.IssueAllTokensForAccount("account_2", success_response); + token_service_.IssueAllTokensForAccount(account_2, success_response); EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); // Check result. - EXPECT_EQ(3u, token_id_pairs_.size()); - EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_); - EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_); - EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_); - EXPECT_EQ("token_1", token_id_pairs_[0].token_); - EXPECT_EQ("token_2", token_id_pairs_[1].token_); - EXPECT_EQ("token_3", token_id_pairs_[2].token_); + EXPECT_EQ(3u, account_id_token_pairs_.size()); + EXPECT_EQ(account_1, account_id_token_pairs_[0].account_id); + EXPECT_EQ(account_2, account_id_token_pairs_[1].account_id); + EXPECT_EQ(account_3, account_id_token_pairs_[2].account_id); + EXPECT_EQ("token_1", account_id_token_pairs_[0].token); + EXPECT_EQ("token_2", account_id_token_pairs_[1].token); + EXPECT_EQ("token_3", account_id_token_pairs_[2].token); } TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsTransientError) { - token_service_.AddAccount("account_1"); - token_service_.AddAccount("account_2"); - token_service_.AddAccount("account_3"); + const CoreAccountId account_1("account_1"); + const CoreAccountId account_2("account_2"); + const CoreAccountId account_3("account_3"); + token_service_.UpdateCredentials(account_1, "refresh_token"); + token_service_.UpdateCredentials(account_2, "refresh_token"); + token_service_.UpdateCredentials(account_3, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = - CreateFetcher({"account_1", "account_2", "account_3"}); + CreateFetcher({account_1, account_2, account_3}); // Connection failures will be retried. token_service_.IssueErrorForAllPendingRequestsForAccount( - "account_1", + account_1, GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); token_service_.IssueErrorForAllPendingRequestsForAccount( - "account_2", + account_2, GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); token_service_.IssueErrorForAllPendingRequestsForAccount( - "account_3", + account_3, GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); // Success on retry. OAuth2AccessTokenConsumer::TokenResponse success_response; success_response.access_token = kAccessToken; success_response.access_token = "token_1"; - token_service_.IssueAllTokensForAccount("account_1", success_response); + token_service_.IssueAllTokensForAccount(account_1, success_response); success_response.access_token = "token_2"; - token_service_.IssueAllTokensForAccount("account_2", success_response); + token_service_.IssueAllTokensForAccount(account_2, success_response); EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); success_response.access_token = "token_3"; - token_service_.IssueAllTokensForAccount("account_3", success_response); + token_service_.IssueAllTokensForAccount(account_3, success_response); EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); // Check result. - EXPECT_EQ(3u, token_id_pairs_.size()); - EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_); - EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_); - EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_); - EXPECT_EQ("token_1", token_id_pairs_[0].token_); - EXPECT_EQ("token_2", token_id_pairs_[1].token_); - EXPECT_EQ("token_3", token_id_pairs_[2].token_); + EXPECT_EQ(3u, account_id_token_pairs_.size()); + EXPECT_EQ(account_1, account_id_token_pairs_[0].account_id); + EXPECT_EQ(account_2, account_id_token_pairs_[1].account_id); + EXPECT_EQ(account_3, account_id_token_pairs_[2].account_id); + EXPECT_EQ("token_1", account_id_token_pairs_[0].token); + EXPECT_EQ("token_2", account_id_token_pairs_[1].token); + EXPECT_EQ("token_3", account_id_token_pairs_[2].token); } TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsPersistentError) { - token_service_.AddAccount("account_1"); - token_service_.AddAccount("account_2"); - token_service_.AddAccount("account_3"); + const CoreAccountId account_1("account_1"); + const CoreAccountId account_2("account_2"); + const CoreAccountId account_3("account_3"); + token_service_.UpdateCredentials(account_1, "refresh_token"); + token_service_.UpdateCredentials(account_2, "refresh_token"); + token_service_.UpdateCredentials(account_3, "refresh_token"); std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = - CreateFetcher({"account_1", "account_2", "account_3"}); + CreateFetcher({account_1, account_2, account_3}); EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); token_service_.IssueErrorForAllPendingRequestsForAccount( - "account_2", + account_2, GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); // Fail as soon as one of the accounts is in error. EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus()); diff --git a/chromium/components/signin/core/browser/signin_manager_base.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc index 37ee80721d5..91105525fcc 100644 --- a/chromium/components/signin/core/browser/signin_manager_base.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc @@ -2,52 +2,51 @@ // 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/signin_manager_base.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include <memory> #include <string> +#include <utility> #include <vector> #include "base/command_line.h" -#include "base/memory/ref_counted.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/signin_switches.h" -#include "google_apis/gaia/gaia_auth_util.h" -#include "google_apis/gaia/gaia_constants.h" -#include "google_apis/gaia/gaia_urls.h" - -SigninManagerBase::SigninManagerBase( +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/signin_switches.h" +#include "components/signin/public/identity_manager/account_info.h" + +PrimaryAccountManager::PrimaryAccountManager( SigninClient* client, ProfileOAuth2TokenService* token_service, AccountTrackerService* account_tracker_service, - GaiaCookieManagerService* cookie_manager_service, - signin::AccountConsistencyMethod account_consistency) + signin::AccountConsistencyMethod account_consistency, + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager) : client_(client), token_service_(token_service), account_tracker_service_(account_tracker_service), initialized_(false), +#if !defined(OS_CHROMEOS) account_consistency_(account_consistency), - weak_pointer_factory_(this) { +#endif + policy_manager_(std::move(policy_manager)) { DCHECK(client_); DCHECK(account_tracker_service_); } -SigninManagerBase::~SigninManagerBase() { - DCHECK(!observer_); +PrimaryAccountManager::~PrimaryAccountManager() { + token_service_->RemoveObserver(this); } // static -void SigninManagerBase::RegisterProfilePrefs(PrefRegistrySimple* registry) { +void PrimaryAccountManager::RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(prefs::kGoogleServicesHostedDomain, std::string()); registry->RegisterStringPref(prefs::kGoogleServicesLastAccountId, @@ -60,8 +59,6 @@ void SigninManagerBase::RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterBooleanPref(prefs::kAutologinEnabled, true); registry->RegisterListPref(prefs::kReverseAutologinRejectedEmailList); registry->RegisterBooleanPref(prefs::kSigninAllowed, true); - registry->RegisterInt64Pref(prefs::kSignedInTime, - base::Time().ToInternalValue()); registry->RegisterBooleanPref(prefs::kSignedInWithCredentialProvider, false); // Deprecated prefs: will be removed in a future release. @@ -69,12 +66,12 @@ void SigninManagerBase::RegisterProfilePrefs(PrefRegistrySimple* registry) { } // static -void SigninManagerBase::RegisterPrefs(PrefRegistrySimple* registry) { +void PrimaryAccountManager::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(prefs::kGoogleServicesUsernamePattern, std::string()); } -void SigninManagerBase::Initialize(PrefService* local_state) { +void PrimaryAccountManager::Initialize(PrefService* local_state) { // Should never call Initialize() twice. DCHECK(!IsInitialized()); initialized_ = true; @@ -89,7 +86,7 @@ void SigninManagerBase::Initialize(PrefService* local_state) { client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); } - std::string account_id = + std::string pref_account_id = client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId); // Handle backward compatibility: if kGoogleServicesAccountId is empty, but @@ -97,7 +94,7 @@ void SigninManagerBase::Initialize(PrefService* local_state) { // be updated. kGoogleServicesUserAccountId should not be empty, and contains // the gaia_id. Use both properties to prime the account tracker before // proceeding. - if (account_id.empty()) { + if (pref_account_id.empty()) { std::string pref_account_username = client_->GetPrefs()->GetString(prefs::kGoogleServicesUsername); if (!pref_account_username.empty()) { @@ -123,13 +120,15 @@ void SigninManagerBase::Initialize(PrefService* local_state) { // is correct. After the migration, the returned value will be empty, // which means the user is essentially signed out. // TODO(rogerta): may want to show a toast or something. - account_id = account_tracker_service_->SeedAccountInfo( - pref_gaia_id, pref_account_username); + pref_account_id = + account_tracker_service_ + ->SeedAccountInfo(pref_gaia_id, pref_account_username) + .id; // Set account id before removing obsolete user name in case crash in the // middle. client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId, - account_id); + pref_account_id); // Now remove obsolete preferences. client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername); @@ -140,51 +139,54 @@ void SigninManagerBase::Initialize(PrefService* local_state) { // kGoogleServicesAccountId. } - if (!account_id.empty()) { + if (!pref_account_id.empty()) { if (account_tracker_service_->GetMigrationState() == AccountTrackerService::MIGRATION_IN_PROGRESS) { AccountInfo account_info = - account_tracker_service_->FindAccountInfoByEmail(account_id); + account_tracker_service_->FindAccountInfoByEmail(pref_account_id); // |account_info.gaia| could be empty if |account_id| is already gaia id. if (!account_info.gaia.empty()) { - account_id = account_info.gaia; + pref_account_id = account_info.gaia; client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId, - account_id); + pref_account_id); } } - SetAuthenticatedAccountId(account_id); + SetAuthenticatedAccountId(CoreAccountId(pref_account_id)); + } + if (policy_manager_) { + policy_manager_->InitializePolicy(local_state, this); } - FinalizeInitBeforeLoadingRefreshTokens(local_state); - token_service()->LoadCredentials(GetAuthenticatedAccountId()); + // It is important to only load credentials after starting to observe the + // token service. + token_service_->AddObserver(this); + token_service_->LoadCredentials(GetAuthenticatedAccountId()); } -void SigninManagerBase::FinalizeInitBeforeLoadingRefreshTokens( - PrefService* local_state) {} - -bool SigninManagerBase::IsInitialized() const { +bool PrimaryAccountManager::IsInitialized() const { return initialized_; } -AccountInfo SigninManagerBase::GetAuthenticatedAccountInfo() const { +AccountInfo PrimaryAccountManager::GetAuthenticatedAccountInfo() const { return account_tracker_service_->GetAccountInfo(GetAuthenticatedAccountId()); } -const std::string& SigninManagerBase::GetAuthenticatedAccountId() const { +const CoreAccountId& PrimaryAccountManager::GetAuthenticatedAccountId() const { return authenticated_account_id_; } -void SigninManagerBase::SetAuthenticatedAccountInfo(const std::string& gaia_id, - const std::string& email) { +void PrimaryAccountManager::SetAuthenticatedAccountInfo( + const std::string& gaia_id, + const std::string& email) { DCHECK(!gaia_id.empty()); DCHECK(!email.empty()); - std::string account_id = + CoreAccountId account_id = account_tracker_service_->SeedAccountInfo(gaia_id, email); SetAuthenticatedAccountId(account_id); } -void SigninManagerBase::SetAuthenticatedAccountId( - const std::string& account_id) { +void PrimaryAccountManager::SetAuthenticatedAccountId( + const CoreAccountId& account_id) { DCHECK(!account_id.empty()); if (!authenticated_account_id_.empty()) { DCHECK_EQ(account_id, authenticated_account_id_) @@ -196,10 +198,11 @@ void SigninManagerBase::SetAuthenticatedAccountId( std::string pref_account_id = client_->GetPrefs()->GetString(prefs::kGoogleServicesAccountId); - DCHECK(pref_account_id.empty() || pref_account_id == account_id) + DCHECK(pref_account_id.empty() || pref_account_id == account_id.id) << "account_id=" << account_id << " pref_account_id=" << pref_account_id; authenticated_account_id_ = account_id; - client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId, account_id); + client_->GetPrefs()->SetString(prefs::kGoogleServicesAccountId, + account_id.id); // This preference is set so that code on I/O thread has access to the // Gaia id of the signed in user. @@ -216,7 +219,7 @@ void SigninManagerBase::SetAuthenticatedAccountId( // user is signed in the corresponding preferences should match. Doing it here // as opposed to on signin allows us to catch the upgrade scenario. client_->GetPrefs()->SetString(prefs::kGoogleServicesLastAccountId, - account_id); + account_id.id); client_->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername, info.email); @@ -224,34 +227,67 @@ void SigninManagerBase::SetAuthenticatedAccountId( // if Chrome crashes before the next commit interval. client_->GetPrefs()->CommitPendingWrite(); - if (observer_) { - observer_->AuthenticatedAccountSet(info); + if (on_authenticated_account_set_callback_) { + on_authenticated_account_set_callback_.Run(info); } } -void SigninManagerBase::ClearAuthenticatedAccountId() { - authenticated_account_id_.clear(); - if (observer_) { - observer_->AuthenticatedAccountCleared(); +void PrimaryAccountManager::ClearAuthenticatedAccountId() { + authenticated_account_id_ = CoreAccountId(); + if (on_authenticated_account_cleared_callback_) { + on_authenticated_account_cleared_callback_.Run(); } } -bool SigninManagerBase::IsAuthenticated() const { +bool PrimaryAccountManager::IsAuthenticated() const { return !authenticated_account_id_.empty(); } -void SigninManagerBase::SetObserver(Observer* observer) { - DCHECK(!observer_) << "SetObserver shouldn't be called multiple times."; - observer_ = observer; +void PrimaryAccountManager::SetGoogleSigninSucceededCallback( + AccountSigninCallback callback) { + DCHECK(!on_google_signin_succeeded_callback_) + << "GoogleSigninSucceededCallback shouldn't be set multiple times."; + on_google_signin_succeeded_callback_ = callback; +} + +#if !defined(OS_CHROMEOS) +void PrimaryAccountManager::SetGoogleSignedOutCallback( + AccountSigninCallback callback) { + DCHECK(!on_google_signed_out_callback_) + << "GoogleSignedOutCallback shouldn't be set multiple times."; + on_google_signed_out_callback_ = callback; +} +#endif // !defined(OS_CHROMEOS) + +void PrimaryAccountManager::SetAuthenticatedAccountSetCallback( + AccountSigninCallback callback) { + DCHECK(!on_authenticated_account_set_callback_) + << "AuthenticatedAccountSetCallback shouldn't be set multiple times."; + on_authenticated_account_set_callback_ = callback; +} + +void PrimaryAccountManager::SetAuthenticatedAccountClearedCallback( + AccountClearedCallback callback) { + DCHECK(!on_authenticated_account_cleared_callback_) + << "AuthenticatedAccountClearedCallback shouldn't be set multiple times."; + on_authenticated_account_cleared_callback_ = callback; } -void SigninManagerBase::ClearObserver() { - DCHECK(observer_); - observer_ = nullptr; +void PrimaryAccountManager::SignIn(const std::string& username) { + AccountInfo info = account_tracker_service_->FindAccountInfoByEmail(username); + DCHECK(!info.gaia.empty()); + DCHECK(!info.email.empty()); + + bool reauth_in_progress = IsAuthenticated(); + + SetAuthenticatedAccountInfo(info.gaia, info.email); + + if (!reauth_in_progress && on_google_signin_succeeded_callback_) + on_google_signin_succeeded_callback_.Run(GetAuthenticatedAccountInfo()); } #if !defined(OS_CHROMEOS) -void SigninManagerBase::SignOut( +void PrimaryAccountManager::SignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { RemoveAccountsOption remove_option = @@ -261,38 +297,43 @@ void SigninManagerBase::SignOut( StartSignOut(signout_source_metric, signout_delete_metric, remove_option); } -void SigninManagerBase::SignOutAndRemoveAllAccounts( +void PrimaryAccountManager::SignOutAndRemoveAllAccounts( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { StartSignOut(signout_source_metric, signout_delete_metric, RemoveAccountsOption::kRemoveAllAccounts); } -void SigninManagerBase::SignOutAndKeepAllAccounts( +void PrimaryAccountManager::SignOutAndKeepAllAccounts( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric) { StartSignOut(signout_source_metric, signout_delete_metric, RemoveAccountsOption::kKeepAllAccounts); } -void SigninManagerBase::StartSignOut( +void PrimaryAccountManager::StartSignOut( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, RemoveAccountsOption remove_option) { - signin_client()->PreSignOut( - base::BindOnce(&SigninManagerBase::OnSignoutDecisionReached, + 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_source_metric); } -void SigninManagerBase::OnSignoutDecisionReached( +void PrimaryAccountManager::OnSignoutDecisionReached( signin_metrics::ProfileSignout signout_source_metric, signin_metrics::SignoutDelete signout_delete_metric, RemoveAccountsOption remove_option, SigninClient::SignoutDecision signout_decision) { DCHECK(IsInitialized()); + VLOG(1) << "OnSignoutDecisionReached: " + << (signout_decision == SigninClient::SignoutDecision::ALLOW_SIGNOUT); signin_metrics::LogSignout(signout_source_metric, signout_delete_metric); if (!IsAuthenticated()) { return; @@ -301,28 +342,17 @@ void SigninManagerBase::OnSignoutDecisionReached( // TODO(crbug.com/887756): Consider moving this higher up, or document why // the above blocks are exempt from the |signout_decision| early return. if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) { - DVLOG(1) << "Ignoring attempt to sign out while signout disallowed"; + VLOG(1) << "Ignoring attempt to sign out while signout disallowed"; return; } AccountInfo account_info = GetAuthenticatedAccountInfo(); - const std::string account_id = GetAuthenticatedAccountId(); + const CoreAccountId account_id = GetAuthenticatedAccountId(); const std::string username = account_info.email; - const base::Time signin_time = - base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromMicroseconds( - signin_client()->GetPrefs()->GetInt64(prefs::kSignedInTime))); ClearAuthenticatedAccountId(); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); - signin_client()->GetPrefs()->ClearPref(prefs::kSignedInTime); - - // Determine the duration the user was logged in and log that to UMA. - if (!signin_time.is_null()) { - base::TimeDelta signed_in_duration = base::Time::Now() - signin_time; - UMA_HISTOGRAM_COUNTS_1M("Signin.SignedInDurationBeforeSignout", - signed_in_duration.InMinutes()); - } + client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain); + client_->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId); + client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); // Revoke all tokens before sending signed_out notification, because there // may be components that don't listen for token service events when the @@ -330,15 +360,15 @@ void SigninManagerBase::OnSignoutDecisionReached( switch (remove_option) { case RemoveAccountsOption::kRemoveAllAccounts: VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out"; - token_service()->RevokeAllCredentials( + token_service_->RevokeAllCredentials( signin_metrics::SourceForRefreshTokenOperation:: - kSigninManager_ClearPrimaryAccount); + kPrimaryAccountManager_ClearAccount); break; case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError: - if (token_service()->RefreshTokenHasError(account_id)) - token_service()->RevokeCredentials( + if (token_service_->RefreshTokenHasError(account_id)) + token_service_->RevokeCredentials( account_id, signin_metrics::SourceForRefreshTokenOperation:: - kSigninManager_ClearPrimaryAccount); + kPrimaryAccountManager_ClearAccount); break; case RemoveAccountsOption::kKeepAllAccounts: // Do nothing. @@ -348,9 +378,33 @@ void SigninManagerBase::OnSignoutDecisionReached( FireGoogleSignedOut(account_info); } -void SigninManagerBase::FireGoogleSignedOut(const AccountInfo& account_info) { - if (observer_ != nullptr) { - observer_->GoogleSignedOut(account_info); +void PrimaryAccountManager::FireGoogleSignedOut( + const AccountInfo& account_info) { + if (on_google_signed_out_callback_) { + on_google_signed_out_callback_.Run(account_info); + } +} + +void PrimaryAccountManager::OnRefreshTokensLoaded() { + token_service_->RemoveObserver(this); + + if (account_tracker_service_->GetMigrationState() == + AccountTrackerService::MIGRATION_IN_PROGRESS) { + account_tracker_service_->SetMigrationDone(); + } + + // Remove account information from the account tracker service if needed. + if (token_service_->HasLoadCredentialsFinishedWithNoErrors()) { + std::vector<AccountInfo> accounts_in_tracker_service = + account_tracker_service_->GetAccounts(); + for (const auto& account : accounts_in_tracker_service) { + if (GetAuthenticatedAccountId() != account.account_id && + !token_service_->RefreshTokenIsAvailable(account.account_id)) { + VLOG(0) << "Removed account from account tracker service: " + << account.account_id; + account_tracker_service_->RemoveAccount(account.account_id); + } + } } } #endif // !defined(OS_CHROMEOS) diff --git a/chromium/components/signin/core/browser/signin_manager_base.h b/chromium/components/signin/internal/identity_manager/primary_account_manager.h index 9297ca097fe..7625fe002c3 100644 --- a/chromium/components/signin/core/browser/signin_manager_base.h +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.h @@ -2,100 +2,55 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// The signin manager encapsulates some functionality tracking -// which user is signed in. +// The PrimaryAccountManager encapsulates some functionality tracking +// which account is the primary one. // -// **NOTE** on semantics of SigninManager: +// **NOTE** on semantics of PrimaryAccountManager: // // Once a signin is successful, the username becomes "established" and will not // be cleared until a SignOut operation is performed (persists across -// restarts). Until that happens, the signin manager can still be used to -// refresh credentials, but changing the username is not permitted. +// restarts). Until that happens, the primary account manager can still be used +// to refresh credentials, but changing the username is not permitted. // -// On Chrome OS, because of the existence of other components that handle login -// and signin at a higher level, all that is needed from a SigninManager is -// caching / handling of the "authenticated username" field, and TokenService -// initialization, so that components that depend on these two things -// (i.e on desktop) can continue using it / don't need to change. For this -// reason, SigninManagerBase is all that exists on Chrome OS. For desktop, -// see signin/signin_manager.h. +// On ChromeOS signout is not possible, so that functionality is if-def'd out on +// that platform. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_BASE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_BASE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MANAGER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MANAGER_H_ #include <memory> #include <string> +#include "base/callback.h" #include "base/callback_list.h" -#include "base/compiler_specific.h" -#include "base/logging.h" #include "base/macros.h" -#include "base/observer_list.h" -#include "components/prefs/pref_change_registrar.h" -#include "components/prefs/pref_member.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/signin_client.h" -#include "google_apis/gaia/google_service_auth_error.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_client.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +struct AccountInfo; class AccountTrackerService; -class GaiaCookieManagerService; class PrefRegistrySimple; class PrefService; +class PrimaryAccountPolicyManager; class ProfileOAuth2TokenService; -class SigninClient; -class SigninManagerBase { - public: - class Observer { - public: - // Called when a user signs into Google services such as sync. - // This method is not called during a reauth. - virtual void GoogleSigninSucceeded(const AccountInfo& account_info) {} - - // Called when the currently signed-in user for a user has been signed out. - virtual void GoogleSignedOut(const AccountInfo& account_info) {} - - // Called during the signin as soon as - // SigninManagerBase::authenticated_account_id_ is set. - virtual void AuthenticatedAccountSet(const AccountInfo& account_info) {} - - // Called during the signout as soon as - // SigninManagerBase::authenticated_account_id_ is cleared. - virtual void AuthenticatedAccountCleared() {} +namespace signin_metrics { +enum ProfileSignout : int; +enum class SignoutDelete; +} // namespace signin_metrics - protected: - virtual ~Observer() {} - - private: - // SigninManagers that fire notifications. - friend class SigninManager; - }; - -// On non-ChromeOS platforms, SigninManagerBase should only be instantiated -// via the derived SigninManager class, as the codewise assumes the -// invariant that any SigninManagerBase object can be cast to a -// SigninManager object when not on ChromeOS. Make the constructor private -// and add SigninManager as a friend to support this. -// TODO(883648): Eliminate the need to downcast SigninManagerBase to -// SigninManager and then eliminate this as well. -#if !defined(OS_CHROMEOS) - private: -#endif - SigninManagerBase(SigninClient* client, - ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service, - GaiaCookieManagerService* cookie_manager_service, - signin::AccountConsistencyMethod account_consistency); -#if !defined(OS_CHROMEOS) +class PrimaryAccountManager : public OAuth2TokenServiceObserver { public: -#endif + typedef base::RepeatingCallback<void(const AccountInfo&)> + AccountSigninCallback; + typedef base::RepeatingCallback<void()> AccountClearedCallback; #if !defined(OS_CHROMEOS) // Used to remove accounts from the token service and the account tracker. enum class RemoveAccountsOption { // Do not remove accounts. - kKeepAllAccounts, + kKeepAllAccounts = 0, // Remove all the accounts. kRemoveAllAccounts, // Removes the authenticated account if it is in authentication error. @@ -103,7 +58,13 @@ class SigninManagerBase { }; #endif - virtual ~SigninManagerBase(); + PrimaryAccountManager( + SigninClient* client, + ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + signin::AccountConsistencyMethod account_consistency, + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager); + ~PrimaryAccountManager() override; // Registers per-profile prefs. static void RegisterProfilePrefs(PrefRegistrySimple* registry); @@ -129,7 +90,7 @@ class SigninManagerBase { // normalized email address of the connected account, use // GetAuthenticatedAccountInfo().email. Example: to show the string "Signed // in as XXX" in the hotdog menu. - const std::string& GetAuthenticatedAccountId() const; + const CoreAccountId& GetAuthenticatedAccountId() const; // Sets the authenticated user's Gaia ID and display email. Internally, // this will seed the account information in AccountTrackerService and pick @@ -140,12 +101,27 @@ class SigninManagerBase { // Returns true if there is an authenticated user. bool IsAuthenticated() const; - // Methods to set or clear the observer of signin. - // In practice these should only be used by IdentityManager. - // NOTE: If SetObserver is called, ClearObserver should be called before - // the destruction of SigninManagerBase. - void SetObserver(Observer* observer); - void ClearObserver(); + // If set, this callback will be invoked whenever a user signs into Google + // services such as sync. This callback is not called during a reauth. + void SetGoogleSigninSucceededCallback(AccountSigninCallback callback); + +#if !defined(OS_CHROMEOS) + // If set, this callback will be invoked whenever the currently signed-in user + // for a user has been signed out. + void SetGoogleSignedOutCallback(AccountSigninCallback callback); +#endif + + // If set, this callback will be invoked during the signin as soon as + // PrimaryAccountManager::authenticated_account_id_ is set. + void SetAuthenticatedAccountSetCallback(AccountSigninCallback callback); + + // If set, this callback will be invoked during the signout as soon as + // PrimaryAccountManager::authenticated_account_id_ is cleared. + void SetAuthenticatedAccountClearedCallback(AccountClearedCallback callback); + + // Signs a user in. PrimaryAccountManager assumes that |username| can be used + // to look up the corresponding account_id and gaia_id for this email. + void SignIn(const std::string& username); // Signout API surfaces (not supported on ChromeOS, where signout is not // permitted). @@ -175,19 +151,7 @@ class SigninManagerBase { signin_metrics::SignoutDelete signout_delete_metric); #endif - protected: - SigninClient* signin_client() const { return client_; } - - ProfileOAuth2TokenService* token_service() const { return token_service_; } - - AccountTrackerService* account_tracker_service() const { - return account_tracker_service_; - } - - // Invoked at the end of |Initialize| before the refresh token for the primary - // account is loaded. - virtual void FinalizeInitBeforeLoadingRefreshTokens(PrefService* local_state); - + private: // Sets the authenticated user's account id. // If the user is already authenticated with the same account id, then this // method is a no-op. @@ -195,24 +159,14 @@ class SigninManagerBase { // with a different account (this method will DCHECK in that case). // |account_id| must not be empty. To log the user out, use // ClearAuthenticatedAccountId() instead. - void SetAuthenticatedAccountId(const std::string& account_id); + void SetAuthenticatedAccountId(const CoreAccountId& account_id); // Clears the authenticated user's account id. - // This method is not public because SigninManagerBase does not allow signing - // out by default. Subclasses implementing a sign-out functionality need to - // call this. + // This method is not public because PrimaryAccountManager does not allow + // signing out by default. Subclasses implementing a sign-out functionality + // need to call this. void ClearAuthenticatedAccountId(); - // Observer to notify on signin events. - // There is a DCHECK on destruction that this has been cleared. - Observer* observer_ = nullptr; - - private: - // Added only to allow SigninManager to call the SigninManagerBase - // constructor while disallowing any ad-hoc subclassing of - // SigninManagerBase. - friend class SigninManager; - #if !defined(OS_CHROMEOS) // Starts the sign out process. void StartSignOut(signin_metrics::ProfileSignout signout_source_metric, @@ -228,6 +182,9 @@ class SigninManagerBase { // Send all observers |GoogleSignedOut| notifications. void FireGoogleSignedOut(const AccountInfo& account_info); + + // OAuth2TokenServiceObserver: + void OnRefreshTokensLoaded() override; #endif SigninClient* client_; @@ -241,16 +198,26 @@ class SigninManagerBase { bool initialized_; // Account id after successful authentication. - std::string authenticated_account_id_; + CoreAccountId authenticated_account_id_; + + // Callbacks which will be invoked, if set, for signin-related events. + AccountSigninCallback on_google_signin_succeeded_callback_; +#if !defined(OS_CHROMEOS) + AccountSigninCallback on_google_signed_out_callback_; +#endif + AccountSigninCallback on_authenticated_account_set_callback_; + AccountClearedCallback on_authenticated_account_cleared_callback_; // The list of callbacks notified on shutdown. base::CallbackList<void()> on_shutdown_callback_list_; +#if !defined(OS_CHROMEOS) signin::AccountConsistencyMethod account_consistency_; +#endif - base::WeakPtrFactory<SigninManagerBase> weak_pointer_factory_; + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager_; - DISALLOW_COPY_AND_ASSIGN(SigninManagerBase); + DISALLOW_COPY_AND_ASSIGN(PrimaryAccountManager); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_BASE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MANAGER_H_ diff --git a/chromium/components/signin/core/browser/signin_manager_unittest.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc index f6f77f798d4..8549cd814ff 100644 --- a/chromium/components/signin/core/browser/signin_manager_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager_unittest.cc @@ -2,87 +2,62 @@ // 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/signin_manager.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" #include <memory> +#include <string> #include <utility> #include <vector> #include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" -#include "build/build_config.h" #include "components/image_fetcher/core/fake_image_decoder.h" -#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/prefs/testing_pref_service.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_fetcher_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/device_id_helper.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" #include "components/sync_preferences/testing_pref_service_syncable.h" -#include "google_apis/gaia/fake_oauth2_token_service_delegate.h" -#include "google_apis/gaia/gaia_urls.h" -#include "net/cookies/cookie_monster.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { +#if !defined(OS_CHROMEOS) +#include "components/signin/internal/identity_manager/primary_account_policy_manager_impl.h" +#endif -class TestSigninManagerObserver : public SigninManagerBase::Observer { +class PrimaryAccountManagerTest : public testing::Test { public: - TestSigninManagerObserver() : num_successful_signins_(0), num_signouts_(0) {} - - ~TestSigninManagerObserver() override {} - - int num_successful_signins_; - int num_signouts_; - - private: - void GoogleSigninSucceeded(const AccountInfo& account_info) override { - num_successful_signins_++; - } - - void GoogleSignedOut(const AccountInfo& account_info) override { - num_signouts_++; - } -}; - -} // namespace - -class SigninManagerTest : public testing::Test { - public: - SigninManagerTest() + PrimaryAccountManagerTest() : test_signin_client_(&user_prefs_), - token_service_(&user_prefs_, - std::make_unique<FakeOAuth2TokenServiceDelegate>()), - cookie_manager_service_(&token_service_, &test_signin_client_), - account_consistency_(signin::AccountConsistencyMethod::kDisabled) { + token_service_( + &user_prefs_, + std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()), + account_consistency_(signin::AccountConsistencyMethod::kDisabled), + num_successful_signins_(0), + num_signouts_(0) { AccountFetcherService::RegisterPrefs(user_prefs_.registry()); AccountTrackerService::RegisterPrefs(user_prefs_.registry()); ProfileOAuth2TokenService::RegisterProfilePrefs(user_prefs_.registry()); - SigninManagerBase::RegisterProfilePrefs(user_prefs_.registry()); - SigninManagerBase::RegisterPrefs(local_state_.registry()); + PrimaryAccountManager::RegisterProfilePrefs(user_prefs_.registry()); + PrimaryAccountManager::RegisterPrefs(local_state_.registry()); account_tracker_.Initialize(&user_prefs_, base::FilePath()); account_fetcher_.Initialize( &test_signin_client_, &token_service_, &account_tracker_, std::make_unique<image_fetcher::FakeImageDecoder>()); } - ~SigninManagerTest() override { + ~PrimaryAccountManagerTest() override { if (manager_) { ShutDownManager(); } token_service_.Shutdown(); test_signin_client_.Shutdown(); - cookie_manager_service_.Shutdown(); account_tracker_.Shutdown(); account_fetcher_.Shutdown(); } @@ -94,28 +69,46 @@ class SigninManagerTest : public testing::Test { PrefService* prefs() { return &user_prefs_; } // Seed the account tracker with information from logged in user. Normally - // this is done by UI code before calling SigninManager. Returns the string - // to use as the account_id. - std::string AddToAccountTracker(const std::string& gaia_id, - const std::string& email) { + // this is done by UI code before calling PrimaryAccountManager. + // Returns the string to use as the account_id. + CoreAccountId AddToAccountTracker(const std::string& gaia_id, + const std::string& email) { account_tracker_.SeedAccountInfo(gaia_id, email); return account_tracker_.PickAccountIdForAccount(gaia_id, email); } - // Create a naked signin manager if integration with PKSs is not needed. - void CreateSigninManager() { + void CreatePrimaryAccountManager() { DCHECK(!manager_); - manager_ = std::make_unique<SigninManager>( + // Supply the primary account manager with a policy manager to reflect + // production usage: null on ChromeOS, a PrimaryAccountPolicyManagerImpl on + // other platforms. + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager; +#if !defined(OS_CHROMEOS) + policy_manager = + std::make_unique<PrimaryAccountPolicyManagerImpl>(&test_signin_client_); + policy_manager_ = + static_cast<PrimaryAccountPolicyManagerImpl*>(policy_manager.get()); +#endif + + manager_ = std::make_unique<PrimaryAccountManager>( &test_signin_client_, &token_service_, &account_tracker_, - &cookie_manager_service_, account_consistency_); + account_consistency_, std::move(policy_manager)); manager_->Initialize(&local_state_); - manager_->SetObserver(&test_observer_); + + // PrimaryAccountManagerTest will outlive the PrimaryAccountManager, so + // base::Unretained is safe. + manager_->SetGoogleSigninSucceededCallback( + base::BindRepeating(&PrimaryAccountManagerTest::GoogleSigninSucceeded, + base::Unretained(this))); +#if !defined(OS_CHROMEOS) + manager_->SetGoogleSignedOutCallback(base::BindRepeating( + &PrimaryAccountManagerTest::GoogleSignedOut, base::Unretained(this))); +#endif } // Shuts down |manager_|. void ShutDownManager() { DCHECK(manager_); - manager_->ClearObserver(); manager_.reset(); } @@ -128,27 +121,37 @@ class SigninManagerTest : public testing::Test { manager_->GetAuthenticatedAccountId())); // Should go into token service and stop. - EXPECT_EQ(1, test_observer_.num_successful_signins_); + EXPECT_EQ(1, num_successful_signins_); } + void GoogleSigninSucceeded(const AccountInfo& account_info) { + num_successful_signins_++; + } + + void GoogleSignedOut(const AccountInfo& account_info) { num_signouts_++; } + base::test::ScopedTaskEnvironment task_environment_; sync_preferences::TestingPrefServiceSyncable user_prefs_; TestingPrefServiceSimple local_state_; TestSigninClient test_signin_client_; ProfileOAuth2TokenService token_service_; AccountTrackerService account_tracker_; - GaiaCookieManagerService cookie_manager_service_; AccountFetcherService account_fetcher_; - std::unique_ptr<SigninManager> manager_; - TestSigninManagerObserver test_observer_; +#if !defined(OS_CHROMEOS) + PrimaryAccountPolicyManagerImpl* policy_manager_; +#endif + std::unique_ptr<PrimaryAccountManager> manager_; std::vector<std::string> oauth_tokens_fetched_; std::vector<std::string> cookies_; signin::AccountConsistencyMethod account_consistency_; + int num_successful_signins_; + int num_signouts_; }; -TEST_F(SigninManagerTest, SignOut) { - CreateSigninManager(); - std::string main_account_id = +#if !defined(OS_CHROMEOS) +TEST_F(PrimaryAccountManagerTest, SignOut) { + CreatePrimaryAccountManager(); + CoreAccountId main_account_id = AddToAccountTracker("account_id", "user@gmail.com"); manager_->SignIn("user@gmail.com"); manager_->SignOut(signin_metrics::SIGNOUT_TEST, @@ -158,17 +161,17 @@ TEST_F(SigninManagerTest, SignOut) { EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty()); // Should not be persisted anymore ShutDownManager(); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_FALSE(manager_->IsAuthenticated()); EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty()); EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty()); } -TEST_F(SigninManagerTest, SignOutRevoke) { - CreateSigninManager(); - std::string main_account_id = +TEST_F(PrimaryAccountManagerTest, SignOutRevoke) { + CreatePrimaryAccountManager(); + CoreAccountId main_account_id = AddToAccountTracker("main_id", "user@gmail.com"); - std::string other_account_id = + CoreAccountId other_account_id = AddToAccountTracker("other_id", "other@gmail.com"); token_service_.UpdateCredentials(main_account_id, "token"); token_service_.UpdateCredentials(other_account_id, "token"); @@ -184,12 +187,12 @@ TEST_F(SigninManagerTest, SignOutRevoke) { EXPECT_TRUE(token_service_.GetAccounts().empty()); } -TEST_F(SigninManagerTest, SignOutDiceNoRevoke) { +TEST_F(PrimaryAccountManagerTest, SignOutDiceNoRevoke) { account_consistency_ = signin::AccountConsistencyMethod::kDice; - CreateSigninManager(); - std::string main_account_id = + CreatePrimaryAccountManager(); + CoreAccountId main_account_id = AddToAccountTracker("main_id", "user@gmail.com"); - std::string other_account_id = + CoreAccountId other_account_id = AddToAccountTracker("other_id", "other@gmail.com"); token_service_.UpdateCredentials(main_account_id, "token"); token_service_.UpdateCredentials(other_account_id, "token"); @@ -202,14 +205,14 @@ TEST_F(SigninManagerTest, SignOutDiceNoRevoke) { // Tokens are not revoked. EXPECT_FALSE(manager_->IsAuthenticated()); - std::vector<std::string> expected_tokens = {main_account_id, - other_account_id}; + std::vector<CoreAccountId> expected_tokens = {main_account_id, + other_account_id}; EXPECT_EQ(expected_tokens, token_service_.GetAccounts()); } -TEST_F(SigninManagerTest, SignOutDiceWithError) { +TEST_F(PrimaryAccountManagerTest, SignOutDiceWithError) { account_consistency_ = signin::AccountConsistencyMethod::kDice; - CreateSigninManager(); + CreatePrimaryAccountManager(); std::string main_account_id = AddToAccountTracker("main_id", "user@gmail.com"); std::string other_account_id = @@ -233,12 +236,12 @@ TEST_F(SigninManagerTest, SignOutDiceWithError) { // Only main token is revoked. EXPECT_FALSE(manager_->IsAuthenticated()); - std::vector<std::string> expected_tokens = {other_account_id}; + std::vector<CoreAccountId> expected_tokens = {other_account_id}; EXPECT_EQ(expected_tokens, token_service_.GetAccounts()); } -TEST_F(SigninManagerTest, SignOutWhileProhibited) { - CreateSigninManager(); +TEST_F(PrimaryAccountManagerTest, SignOutWhileProhibited) { + CreatePrimaryAccountManager(); EXPECT_FALSE(manager_->IsAuthenticated()); EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty()); EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty()); @@ -254,44 +257,21 @@ TEST_F(SigninManagerTest, SignOutWhileProhibited) { EXPECT_FALSE(manager_->IsAuthenticated()); } -TEST_F(SigninManagerTest, Prohibited) { - local_state_.SetString(prefs::kGoogleServicesUsernamePattern, - ".*@google.com"); - CreateSigninManager(); - EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com")); - EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com")); - EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com")); - EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com")); - EXPECT_FALSE(manager_->IsAllowedUsername(std::string())); -} - -TEST_F(SigninManagerTest, TestAlternateWildcard) { - // Test to make sure we accept "*@google.com" as a pattern (treat it as if - // the admin entered ".*@google.com"). - local_state_.SetString(prefs::kGoogleServicesUsernamePattern, "*@google.com"); - CreateSigninManager(); - EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com")); - EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com")); - EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com")); - EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com")); - EXPECT_FALSE(manager_->IsAllowedUsername(std::string())); -} - -TEST_F(SigninManagerTest, ProhibitedAtStartup) { +TEST_F(PrimaryAccountManagerTest, ProhibitedAtStartup) { std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com"); user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id); local_state_.SetString(prefs::kGoogleServicesUsernamePattern, ".*@google.com"); - CreateSigninManager(); + CreatePrimaryAccountManager(); // Currently signed in user is prohibited by policy, so should be signed out. EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ("", manager_->GetAuthenticatedAccountId()); } -TEST_F(SigninManagerTest, ProhibitedAfterStartup) { +TEST_F(PrimaryAccountManagerTest, ProhibitedAfterStartup) { std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com"); user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId()); // Update the profile - user should be signed out. @@ -300,53 +280,57 @@ TEST_F(SigninManagerTest, ProhibitedAfterStartup) { EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ("", manager_->GetAuthenticatedAccountId()); } +#endif -TEST_F(SigninManagerTest, ExternalSignIn) { - CreateSigninManager(); +TEST_F(PrimaryAccountManagerTest, ExternalSignIn) { + CreatePrimaryAccountManager(); EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ("", manager_->GetAuthenticatedAccountId()); - EXPECT_EQ(0, test_observer_.num_successful_signins_); + EXPECT_EQ(0, num_successful_signins_); std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com"); manager_->SignIn("user@gmail.com"); - EXPECT_EQ(1, test_observer_.num_successful_signins_); + EXPECT_EQ(1, num_successful_signins_); EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId()); } -TEST_F(SigninManagerTest, ExternalSignIn_ReauthShouldNotSendNotification) { - CreateSigninManager(); +TEST_F(PrimaryAccountManagerTest, + ExternalSignIn_ReauthShouldNotSendNotification) { + CreatePrimaryAccountManager(); EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ("", manager_->GetAuthenticatedAccountId()); - EXPECT_EQ(0, test_observer_.num_successful_signins_); + EXPECT_EQ(0, num_successful_signins_); std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com"); manager_->SignIn("user@gmail.com"); - EXPECT_EQ(1, test_observer_.num_successful_signins_); + EXPECT_EQ(1, num_successful_signins_); EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId()); manager_->SignIn("user@gmail.com"); - EXPECT_EQ(1, test_observer_.num_successful_signins_); + EXPECT_EQ(1, num_successful_signins_); EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId()); } -TEST_F(SigninManagerTest, SigninNotAllowed) { +#if !defined(OS_CHROMEOS) +TEST_F(PrimaryAccountManagerTest, SigninNotAllowed) { std::string user("user@google.com"); std::string account_id = AddToAccountTracker("gaia_id", user); user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id); user_prefs_.SetBoolean(prefs::kSigninAllowed, false); - CreateSigninManager(); + CreatePrimaryAccountManager(); // Currently signing in is prohibited by policy, so should be signed out. EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email); EXPECT_EQ("", manager_->GetAuthenticatedAccountId()); } +#endif -TEST_F(SigninManagerTest, UpgradeToNewPrefs) { +TEST_F(PrimaryAccountManagerTest, UpgradeToNewPrefs) { user_prefs_.SetString(prefs::kGoogleServicesUsername, "user@gmail.com"); user_prefs_.SetString(prefs::kGoogleServicesUserAccountId, "account_id"); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email); if (account_tracker()->GetMigrationState() == @@ -370,13 +354,13 @@ TEST_F(SigninManagerTest, UpgradeToNewPrefs) { EXPECT_EQ("account_id", info.gaia); } -TEST_F(SigninManagerTest, CanonicalizesPrefs) { +TEST_F(PrimaryAccountManagerTest, CanonicalizesPrefs) { // This unit test is not needed after migrating to gaia id. if (account_tracker()->GetMigrationState() == AccountTrackerService::MIGRATION_NOT_STARTED) { user_prefs_.SetString(prefs::kGoogleServicesUsername, "user.C@gmail.com"); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ("user.C@gmail.com", manager_->GetAuthenticatedAccountInfo().email); @@ -395,7 +379,7 @@ TEST_F(SigninManagerTest, CanonicalizesPrefs) { } } -TEST_F(SigninManagerTest, GaiaIdMigration) { +TEST_F(PrimaryAccountManagerTest, GaiaIdMigration) { if (account_tracker()->GetMigrationState() != AccountTrackerService::MIGRATION_NOT_STARTED) { std::string email = "user@gmail.com"; @@ -417,14 +401,14 @@ TEST_F(SigninManagerTest, GaiaIdMigration) { client_prefs->SetString(prefs::kGoogleServicesAccountId, email); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ(gaia_id, manager_->GetAuthenticatedAccountId()); EXPECT_EQ(gaia_id, user_prefs_.GetString(prefs::kGoogleServicesAccountId)); } } -TEST_F(SigninManagerTest, VeryOldProfileGaiaIdMigration) { +TEST_F(PrimaryAccountManagerTest, VeryOldProfileGaiaIdMigration) { if (account_tracker()->GetMigrationState() != AccountTrackerService::MIGRATION_NOT_STARTED) { std::string email = "user@gmail.com"; @@ -447,13 +431,13 @@ TEST_F(SigninManagerTest, VeryOldProfileGaiaIdMigration) { client_prefs->ClearPref(prefs::kGoogleServicesAccountId); client_prefs->SetString(prefs::kGoogleServicesUsername, email); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ(gaia_id, manager_->GetAuthenticatedAccountId()); EXPECT_EQ(gaia_id, user_prefs_.GetString(prefs::kGoogleServicesAccountId)); } } -TEST_F(SigninManagerTest, GaiaIdMigrationCrashInTheMiddle) { +TEST_F(PrimaryAccountManagerTest, GaiaIdMigrationCrashInTheMiddle) { if (account_tracker()->GetMigrationState() != AccountTrackerService::MIGRATION_NOT_STARTED) { std::string email = "user@gmail.com"; @@ -475,7 +459,7 @@ TEST_F(SigninManagerTest, GaiaIdMigrationCrashInTheMiddle) { client_prefs->SetString(prefs::kGoogleServicesAccountId, gaia_id); - CreateSigninManager(); + CreatePrimaryAccountManager(); EXPECT_EQ(gaia_id, manager_->GetAuthenticatedAccountId()); EXPECT_EQ(gaia_id, user_prefs_.GetString(prefs::kGoogleServicesAccountId)); 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 new file mode 100644 index 00000000000..f9d1eba03ec --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc @@ -0,0 +1,90 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/internal/identity_manager/primary_account_mutator_impl.h" + +#include <string> + +#include "base/logging.h" +#include "components/prefs/pref_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "google_apis/gaia/core_account_id.h" + +namespace signin { + +PrimaryAccountMutatorImpl::PrimaryAccountMutatorImpl( + AccountTrackerService* account_tracker, + PrimaryAccountManager* primary_account_manager, + PrefService* pref_service) + : account_tracker_(account_tracker), + primary_account_manager_(primary_account_manager), + pref_service_(pref_service) { + DCHECK(account_tracker_); + DCHECK(primary_account_manager_); + DCHECK(pref_service_); +} + +PrimaryAccountMutatorImpl::~PrimaryAccountMutatorImpl() {} + +bool PrimaryAccountMutatorImpl::SetPrimaryAccount( + const CoreAccountId& account_id) { + AccountInfo account_info = account_tracker_->GetAccountInfo(account_id); + +#if !defined(OS_CHROMEOS) + if (!pref_service_->GetBoolean(prefs::kSigninAllowed)) + return false; + + if (primary_account_manager_->IsAuthenticated()) + return false; + + if (account_info.account_id != account_id || account_info.email.empty()) + return false; + + // TODO(crbug.com/889899): should check that the account email is allowed. +#endif + + primary_account_manager_->SignIn(account_info.email); + return true; +} + +#if defined(OS_CHROMEOS) +bool PrimaryAccountMutatorImpl::SetPrimaryAccountAndUpdateAccountInfo( + const std::string& gaia_id, + const std::string& email) { + CoreAccountId account_id = account_tracker_->SeedAccountInfo(gaia_id, email); + SetPrimaryAccount(account_id); + return true; +} +#endif + +#if !defined(OS_CHROMEOS) +bool PrimaryAccountMutatorImpl::ClearPrimaryAccount( + ClearAccountsAction action, + signin_metrics::ProfileSignout source_metric, + signin_metrics::SignoutDelete delete_metric) { + if (!primary_account_manager_->IsAuthenticated()) + return false; + + switch (action) { + case PrimaryAccountMutator::ClearAccountsAction::kDefault: + primary_account_manager_->SignOut(source_metric, delete_metric); + break; + case PrimaryAccountMutator::ClearAccountsAction::kKeepAll: + primary_account_manager_->SignOutAndKeepAllAccounts(source_metric, + delete_metric); + break; + case PrimaryAccountMutator::ClearAccountsAction::kRemoveAll: + primary_account_manager_->SignOutAndRemoveAllAccounts(source_metric, + delete_metric); + break; + } + + return true; +} +#endif + +} // namespace signin 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 new file mode 100644 index 00000000000..f15e6c13f9b --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_IMPL_H_ + +#include <string> + +#include "components/signin/public/identity_manager/primary_account_mutator.h" + +class AccountTrackerService; +class PrefService; +class PrimaryAccountManager; + +namespace signin { + +// Concrete implementation of PrimaryAccountMutator that is based on the +// PrimaryAccountManager API. +class PrimaryAccountMutatorImpl : public PrimaryAccountMutator { + public: + PrimaryAccountMutatorImpl(AccountTrackerService* account_tracker, + PrimaryAccountManager* primary_account_manager, + PrefService* pref_service); + ~PrimaryAccountMutatorImpl() override; + + // PrimaryAccountMutator implementation. + bool SetPrimaryAccount(const CoreAccountId& account_id) override; +#if defined(OS_CHROMEOS) + bool SetPrimaryAccountAndUpdateAccountInfo(const std::string& gaia_id, + const std::string& email) override; +#endif +#if !defined(OS_CHROMEOS) + bool ClearPrimaryAccount( + ClearAccountsAction action, + signin_metrics::ProfileSignout source_metric, + signin_metrics::SignoutDelete delete_metric) override; +#endif + + private: + // Pointers to the services used by the PrimaryAccountMutatorImpl. They + // *must* outlive this instance. + AccountTrackerService* account_tracker_ = nullptr; + PrimaryAccountManager* primary_account_manager_ = nullptr; + PrefService* pref_service_ = nullptr; +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_IMPL_H_ diff --git a/chromium/components/signin/internal/identity_manager/primary_account_policy_manager.h b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager.h new file mode 100644 index 00000000000..d5e1537a39b --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager.h @@ -0,0 +1,29 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_H_ + +#include "base/macros.h" + +class PrefService; +class PrimaryAccountManager; + +class PrimaryAccountPolicyManager { + public: + PrimaryAccountPolicyManager() = default; + virtual ~PrimaryAccountPolicyManager() = default; + + // On platforms where PrimaryAccountManager is responsible for dealing with + // invalid username policy updates, we need to check this during + // initialization and sign the user out. + virtual void InitializePolicy( + PrefService* local_state, + PrimaryAccountManager* primary_account_manager) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(PrimaryAccountPolicyManager); +}; + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_H_ 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 new file mode 100644 index 00000000000..426aa6b4daf --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc @@ -0,0 +1,107 @@ +// 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/internal/identity_manager/primary_account_policy_manager_impl.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/identity_utils.h" + +PrimaryAccountPolicyManagerImpl::PrimaryAccountPolicyManagerImpl( + SigninClient* client) + : client_(client) {} + +PrimaryAccountPolicyManagerImpl::~PrimaryAccountPolicyManagerImpl() { + local_state_pref_registrar_.RemoveAll(); +} + +void PrimaryAccountPolicyManagerImpl::InitializePolicy( + PrefService* local_state, + PrimaryAccountManager* primary_account_manager) { + // local_state can be null during unit tests. + if (local_state) { + 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)); + } + signin_allowed_.Init( + prefs::kSigninAllowed, client_->GetPrefs(), + base::Bind(&PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged, + base::Unretained(this), primary_account_manager)); + + AccountInfo account_info = + primary_account_manager->GetAuthenticatedAccountInfo(); + if (!account_info.account_id.empty() && + (!IsAllowedUsername(account_info.email) || !IsSigninAllowed())) { + // User is signed in, but the username is invalid or signin is no longer + // allowed, so the user must be sign out. + // + // This may happen in the following cases: + // a. The user has toggled off signin allowed in settings. + // b. The administrator changed the policy since the last signin. + // + // Note: The token service has not yet loaded its credentials, so accounts + // cannot be revoked here. + // + // On desktop, when PrimaryAccountManager is initializing, the profile was + // not yet marked with sign out allowed. Therefore sign out is not allowed + // and all calls to SignOut methods are no-op. + // + // TODO(msarda): SignOut methods do not guarantee that sign out can actually + // be done (this depends on whether sign out is allowed). Add a check here + // on desktop to make it clear that SignOut does not do anything. + primary_account_manager->SignOutAndKeepAllAccounts( + signin_metrics::SIGNIN_PREF_CHANGED_DURING_SIGNIN, + signin_metrics::SignoutDelete::IGNORE_METRIC); + } +} + +void PrimaryAccountPolicyManagerImpl::OnGoogleServicesUsernamePatternChanged( + PrimaryAccountManager* primary_account_manager) { + if (primary_account_manager->IsAuthenticated() && + !IsAllowedUsername( + primary_account_manager->GetAuthenticatedAccountInfo().email)) { + // Signed in user is invalid according to the current policy so sign + // the user out. + primary_account_manager->SignOut( + signin_metrics::GOOGLE_SERVICE_NAME_PATTERN_CHANGED, + signin_metrics::SignoutDelete::IGNORE_METRIC); + } +} + +bool PrimaryAccountPolicyManagerImpl::IsSigninAllowed() const { + return signin_allowed_.GetValue(); +} + +void PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged( + PrimaryAccountManager* primary_account_manager) { + if (!IsSigninAllowed() && primary_account_manager->IsAuthenticated()) { + VLOG(0) << "IsSigninAllowed() set to false, signing out the user"; + primary_account_manager->SignOut( + signin_metrics::SIGNOUT_PREF_CHANGED, + signin_metrics::SignoutDelete::IGNORE_METRIC); + } +} + +bool PrimaryAccountPolicyManagerImpl::IsAllowedUsername( + const std::string& username) const { + const PrefService* local_state = local_state_pref_registrar_.prefs(); + + // TODO(crbug.com/908121): We need to deal for now with the fact that many + // unit tests have a null |local_state| passed to InitializePolicy(), in which + // case all usernames are considered 'allowed'. + if (!local_state) + return true; + + return signin::IsUsernameAllowedByPatternFromPrefs(local_state, username); +} diff --git a/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.h b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.h new file mode 100644 index 00000000000..43bce6d345b --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl.h @@ -0,0 +1,62 @@ +// 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_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_IMPL_H_ + +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_member.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager.h" + +class PrefService; +class PrimaryAccountManager; +class SigninClient; + +class PrimaryAccountPolicyManagerImpl : public PrimaryAccountPolicyManager { + public: + explicit PrimaryAccountPolicyManagerImpl(SigninClient* client); + ~PrimaryAccountPolicyManagerImpl() override; + + // PrimaryAccountPolicyManager: + void InitializePolicy( + PrefService* local_state, + PrimaryAccountManager* primary_account_manager) override; + + private: + FRIEND_TEST_ALL_PREFIXES(PrimaryAccountPolicyManagerImplTest, Prohibited); + FRIEND_TEST_ALL_PREFIXES(PrimaryAccountPolicyManagerImplTest, + TestAlternateWildcard); + + // Returns true if a signin to Chrome is allowed (by policy or pref). + bool IsSigninAllowed() const; + + void OnSigninAllowedPrefChanged( + PrimaryAccountManager* primary_account_manager); + void OnGoogleServicesUsernamePatternChanged( + PrimaryAccountManager* primary_account_manager); + + // Returns true if the passed username is allowed by policy. + bool IsAllowedUsername(const std::string& username) const; + + SigninClient* client_; + + // Helper object to listen for changes to signin preferences stored in non- + // profile-specific local prefs (like kGoogleServicesUsernamePattern). + PrefChangeRegistrar local_state_pref_registrar_; + + // Helper object to listen for changes to the signin allowed preference. + BooleanPrefMember signin_allowed_; + + base::WeakPtrFactory<PrimaryAccountPolicyManagerImpl> weak_pointer_factory_{ + this}; + + DISALLOW_COPY_AND_ASSIGN(PrimaryAccountPolicyManagerImpl); +}; + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_POLICY_MANAGER_IMPL_H_ diff --git a/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl_unittest.cc b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl_unittest.cc new file mode 100644 index 00000000000..282eab2ec0e --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/primary_account_policy_manager_impl_unittest.cc @@ -0,0 +1,75 @@ +// 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/internal/identity_manager/primary_account_policy_manager_impl.h" + +#include <memory> +#include <string> + +#include "base/test/scoped_task_environment.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager_impl.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "testing/gtest/include/gtest/gtest.h" + +class PrimaryAccountPolicyManagerImplTest : public testing::Test { + public: + PrimaryAccountPolicyManagerImplTest() + : test_signin_client_(&user_prefs_), + token_service_( + &user_prefs_, + std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()), + primary_account_manager_(&test_signin_client_, + &token_service_, + &account_tracker_, + signin::AccountConsistencyMethod::kDisabled, + nullptr /*policy_manager*/), + policy_manager_(&test_signin_client_) { + PrimaryAccountManager::RegisterProfilePrefs(user_prefs_.registry()); + PrimaryAccountManager::RegisterPrefs(local_state_.registry()); + + policy_manager_.InitializePolicy(&local_state_, &primary_account_manager_); + } + + ~PrimaryAccountPolicyManagerImplTest() override { + test_signin_client_.Shutdown(); + } + + base::test::ScopedTaskEnvironment task_environment_; + sync_preferences::TestingPrefServiceSyncable user_prefs_; + TestingPrefServiceSimple local_state_; + TestSigninClient test_signin_client_; + ProfileOAuth2TokenService token_service_; + AccountTrackerService account_tracker_; + PrimaryAccountManager primary_account_manager_; + PrimaryAccountPolicyManagerImpl policy_manager_; +}; + +TEST_F(PrimaryAccountPolicyManagerImplTest, Prohibited) { + local_state_.SetString(prefs::kGoogleServicesUsernamePattern, + ".*@google.com"); + EXPECT_TRUE(policy_manager_.IsAllowedUsername("test@google.com")); + EXPECT_TRUE(policy_manager_.IsAllowedUsername("happy@google.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername("test@invalid.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername("test@notgoogle.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername(std::string())); +} + +TEST_F(PrimaryAccountPolicyManagerImplTest, TestAlternateWildcard) { + // Test to make sure we accept "*@google.com" as a pattern (treat it as if + // the admin entered ".*@google.com"). + local_state_.SetString(prefs::kGoogleServicesUsernamePattern, "*@google.com"); + EXPECT_TRUE(policy_manager_.IsAllowedUsername("test@google.com")); + EXPECT_TRUE(policy_manager_.IsAllowedUsername("happy@google.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername("test@invalid.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername("test@notgoogle.com")); + EXPECT_FALSE(policy_manager_.IsAllowedUsername(std::string())); +} diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc new file mode 100644 index 00000000000..6d4f5c1d584 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.cc @@ -0,0 +1,476 @@ +// 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. + +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" + +#include "base/auto_reset.h" +#include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "components/signin/public/base/device_id_helper.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/google_service_auth_error.h" +#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.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +using signin_metrics::SourceForRefreshTokenOperation; + +namespace { +std::string SourceToString(SourceForRefreshTokenOperation source) { + switch (source) { + case SourceForRefreshTokenOperation::kUnknown: + return "Unknown"; + case SourceForRefreshTokenOperation::kTokenService_LoadCredentials: + return "TokenService::LoadCredentials"; + case SourceForRefreshTokenOperation::kSupervisedUser_InitSync: + return "SupervisedUser::InitSync"; + case SourceForRefreshTokenOperation::kInlineLoginHandler_Signin: + return "InlineLoginHandler::Signin"; + case SourceForRefreshTokenOperation::kPrimaryAccountManager_ClearAccount: + return "PrimaryAccountManager::ClearAccount"; + case SourceForRefreshTokenOperation:: + kPrimaryAccountManager_LegacyPreDiceSigninFlow: + return "PrimaryAccountManager::LegacyPreDiceSigninFlow"; + case SourceForRefreshTokenOperation::kUserMenu_RemoveAccount: + return "UserMenu::RemoveAccount"; + case SourceForRefreshTokenOperation::kUserMenu_SignOutAllAccounts: + return "UserMenu::SignOutAllAccounts"; + case SourceForRefreshTokenOperation::kSettings_Signout: + return "Settings::Signout"; + case SourceForRefreshTokenOperation::kSettings_PauseSync: + return "Settings::PauseSync"; + case SourceForRefreshTokenOperation:: + kAccountReconcilor_GaiaCookiesDeletedByUser: + return "AccountReconcilor::GaiaCookiesDeletedByUser"; + case SourceForRefreshTokenOperation::kAccountReconcilor_GaiaCookiesUpdated: + return "AccountReconcilor::GaiaCookiesUpdated"; + case SourceForRefreshTokenOperation::kAccountReconcilor_Reconcile: + return "AccountReconcilor::Reconcile"; + case SourceForRefreshTokenOperation::kDiceResponseHandler_Signin: + return "DiceResponseHandler::Signin"; + case SourceForRefreshTokenOperation::kDiceResponseHandler_Signout: + return "DiceResponseHandler::Signout"; + case SourceForRefreshTokenOperation::kDiceTurnOnSyncHelper_Abort: + return "DiceTurnOnSyncHelper::Abort"; + case SourceForRefreshTokenOperation::kMachineLogon_CredentialProvider: + return "MachineLogon::CredentialProvider"; + case SourceForRefreshTokenOperation::kTokenService_ExtractCredentials: + return "TokenService::ExtractCredentials"; + } +} +} // namespace + +ProfileOAuth2TokenService::ProfileOAuth2TokenService( + PrefService* user_prefs, + std::unique_ptr<ProfileOAuth2TokenServiceDelegate> delegate) + : user_prefs_(user_prefs), + delegate_(std::move(delegate)), + all_credentials_loaded_(false) { + DCHECK(user_prefs_); + DCHECK(delegate_); + token_manager_ = std::make_unique<OAuth2AccessTokenManager>( + this /* OAuth2AccessTokenManager::Delegate* */); + AddObserver(this); +} + +ProfileOAuth2TokenService::~ProfileOAuth2TokenService() { + RemoveObserver(this); +} + +std::unique_ptr<OAuth2AccessTokenFetcher> +ProfileOAuth2TokenService::CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) { + return delegate_->CreateAccessTokenFetcher(account_id, url_loader_factory, + consumer); +} + +bool ProfileOAuth2TokenService::FixRequestErrorIfPossible() { + return delegate_->FixRequestErrorIfPossible(); +} + +scoped_refptr<network::SharedURLLoaderFactory> +ProfileOAuth2TokenService::GetURLLoaderFactory() const { + return delegate_->GetURLLoaderFactory(); +} + +void ProfileOAuth2TokenService::OnAccessTokenInvalidated( + const CoreAccountId& account_id, + const std::string& client_id, + const std::set<std::string>& scopes, + const std::string& access_token) { + delegate_->OnAccessTokenInvalidated(account_id, client_id, scopes, + access_token); +} + +void ProfileOAuth2TokenService::OnAccessTokenFetched( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + // Update the auth error state so auth errors are appropriately communicated + // to the user. + delegate_->UpdateAuthError(account_id, error); +} + +bool ProfileOAuth2TokenService::HasRefreshToken( + const CoreAccountId& account_id) const { + return RefreshTokenIsAvailable(account_id); +} + +// static +void ProfileOAuth2TokenService::RegisterProfilePrefs( + PrefRegistrySimple* registry) { +#if defined(OS_IOS) + registry->RegisterBooleanPref(prefs::kTokenServiceExcludeAllSecondaryAccounts, + false); + registry->RegisterListPref(prefs::kTokenServiceExcludedSecondaryAccounts); +#endif + registry->RegisterStringPref(prefs::kGoogleServicesSigninScopedDeviceId, + std::string()); +} + +ProfileOAuth2TokenServiceDelegate* ProfileOAuth2TokenService::GetDelegate() { + return delegate_.get(); +} + +const ProfileOAuth2TokenServiceDelegate* +ProfileOAuth2TokenService::GetDelegate() const { + return delegate_.get(); +} + +void ProfileOAuth2TokenService::AddObserver( + OAuth2TokenServiceObserver* observer) { + delegate_->AddObserver(observer); +} + +void ProfileOAuth2TokenService::RemoveObserver( + OAuth2TokenServiceObserver* observer) { + delegate_->RemoveObserver(observer); +} + +void ProfileOAuth2TokenService::AddAccessTokenDiagnosticsObserver( + OAuth2AccessTokenManager::DiagnosticsObserver* observer) { + token_manager_->AddDiagnosticsObserver(observer); +} + +void ProfileOAuth2TokenService::RemoveAccessTokenDiagnosticsObserver( + OAuth2AccessTokenManager::DiagnosticsObserver* observer) { + token_manager_->RemoveDiagnosticsObserver(observer); +} + +std::unique_ptr<OAuth2AccessTokenManager::Request> +ProfileOAuth2TokenService::StartRequest( + const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer) { + return token_manager_->StartRequest(account_id, scopes, consumer); +} + +std::unique_ptr<OAuth2AccessTokenManager::Request> +ProfileOAuth2TokenService::StartRequestForMultilogin( + const CoreAccountId& account_id, + OAuth2AccessTokenManager::Consumer* consumer) { + const std::string refresh_token = + delegate_->GetTokenForMultilogin(account_id); + if (refresh_token.empty()) { + // If we can't get refresh token from the delegate, start request for access + // token. + OAuth2AccessTokenManager::ScopeSet scopes; + scopes.insert(GaiaConstants::kOAuth1LoginScope); + return token_manager_->StartRequest(account_id, scopes, consumer); + } + std::unique_ptr<OAuth2AccessTokenManager::RequestImpl> request( + new OAuth2AccessTokenManager::RequestImpl(account_id, consumer)); + // Create token response from token. Expiration time and id token do not + // matter and should not be accessed. + OAuth2AccessTokenConsumer::TokenResponse token_response( + refresh_token, base::Time(), std::string()); + // If we can get refresh token from the delegate, inform consumer right away. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&OAuth2AccessTokenManager::RequestImpl::InformConsumer, + request.get()->AsWeakPtr(), + GoogleServiceAuthError(GoogleServiceAuthError::NONE), + token_response)); + return std::move(request); +} + +std::unique_ptr<OAuth2AccessTokenManager::Request> +ProfileOAuth2TokenService::StartRequestForClient( + const CoreAccountId& account_id, + const std::string& client_id, + const std::string& client_secret, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer) { + return token_manager_->StartRequestForClient(account_id, client_id, + client_secret, scopes, consumer); +} + +std::unique_ptr<OAuth2AccessTokenManager::Request> +ProfileOAuth2TokenService::StartRequestWithContext( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer) { + return token_manager_->StartRequestWithContext(account_id, url_loader_factory, + scopes, consumer); +} + +void ProfileOAuth2TokenService::InvalidateAccessToken( + const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes, + const std::string& access_token) { + token_manager_->InvalidateAccessToken(account_id, scopes, access_token); +} + +void ProfileOAuth2TokenService::InvalidateTokenForMultilogin( + const CoreAccountId& failed_account, + const std::string& token) { + OAuth2AccessTokenManager::ScopeSet scopes; + scopes.insert(GaiaConstants::kOAuth1LoginScope); + // Remove from cache. This will have no effect on desktop since token is a + // refresh token and is not in cache. + InvalidateAccessToken(failed_account, scopes, token); + // For desktop refresh tokens can be invalidated directly in delegate. This + // will have no effect on mobile. + delegate_->InvalidateTokenForMultilogin(failed_account); +} + +void ProfileOAuth2TokenService::SetRefreshTokenAvailableFromSourceCallback( + RefreshTokenAvailableFromSourceCallback callback) { + on_refresh_token_available_callback_ = callback; +} + +void ProfileOAuth2TokenService::SetRefreshTokenRevokedFromSourceCallback( + RefreshTokenRevokedFromSourceCallback callback) { + on_refresh_token_revoked_callback_ = callback; +} + +void ProfileOAuth2TokenService::Shutdown() { + CancelAllRequests(); + GetDelegate()->Shutdown(); +} + +void ProfileOAuth2TokenService::LoadCredentials( + const CoreAccountId& primary_account_id) { + DCHECK_EQ(SourceForRefreshTokenOperation::kUnknown, + update_refresh_token_source_); + update_refresh_token_source_ = + SourceForRefreshTokenOperation::kTokenService_LoadCredentials; + GetDelegate()->LoadCredentials(primary_account_id); +} + +void ProfileOAuth2TokenService::UpdateCredentials( + const CoreAccountId& account_id, + const std::string& refresh_token, + SourceForRefreshTokenOperation source) { + base::AutoReset<SourceForRefreshTokenOperation> auto_reset( + &update_refresh_token_source_, source); + GetDelegate()->UpdateCredentials(account_id, refresh_token); +} + +void ProfileOAuth2TokenService::RevokeCredentials( + const CoreAccountId& account_id, + SourceForRefreshTokenOperation source) { + base::AutoReset<SourceForRefreshTokenOperation> auto_reset( + &update_refresh_token_source_, source); + GetDelegate()->RevokeCredentials(account_id); +} + +void ProfileOAuth2TokenService::RevokeAllCredentials( + SourceForRefreshTokenOperation source) { + base::AutoReset<SourceForRefreshTokenOperation> auto_reset( + &update_refresh_token_source_, source); + CancelAllRequests(); + ClearCache(); + GetDelegate()->RevokeAllCredentials(); +} + +const net::BackoffEntry* ProfileOAuth2TokenService::GetDelegateBackoffEntry() { + return GetDelegate()->BackoffEntry(); +} + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +void ProfileOAuth2TokenService::ExtractCredentials( + ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) { + base::AutoReset<SourceForRefreshTokenOperation> auto_reset( + &update_refresh_token_source_, + SourceForRefreshTokenOperation::kTokenService_ExtractCredentials); + GetDelegate()->ExtractCredentials(to_service, account_id); +} +#endif + +bool ProfileOAuth2TokenService::AreAllCredentialsLoaded() const { + return all_credentials_loaded_; +} + +std::vector<CoreAccountId> ProfileOAuth2TokenService::GetAccounts() const { + if (!AreAllCredentialsLoaded()) + return std::vector<CoreAccountId>(); + + return GetDelegate()->GetAccounts(); +} + +bool ProfileOAuth2TokenService::RefreshTokenIsAvailable( + const CoreAccountId& account_id) const { + return delegate_->RefreshTokenIsAvailable(account_id); +} + +bool ProfileOAuth2TokenService::RefreshTokenHasError( + const CoreAccountId& account_id) const { + return GetAuthError(account_id) != GoogleServiceAuthError::AuthErrorNone(); +} + +GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthError( + const CoreAccountId& account_id) const { + GoogleServiceAuthError error = delegate_->GetAuthError(account_id); + DCHECK(!error.IsTransientError()); + return error; +} + +void ProfileOAuth2TokenService::UpdateAuthErrorForTesting( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + GetDelegate()->UpdateAuthError(account_id, error); +} + +int ProfileOAuth2TokenService::GetTokenCacheCountForTesting() { + return token_manager_->token_cache().size(); +} + +void ProfileOAuth2TokenService:: + set_max_authorization_token_fetch_retries_for_testing(int max_retries) { + token_manager_->set_max_authorization_token_fetch_retries_for_testing( + max_retries); +} + +size_t ProfileOAuth2TokenService::GetNumPendingRequestsForTesting( + const std::string& client_id, + const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes) const { + return token_manager_->GetNumPendingRequestsForTesting(client_id, account_id, + scopes); +} + +void ProfileOAuth2TokenService::OverrideAccessTokenManagerForTesting( + std::unique_ptr<OAuth2AccessTokenManager> token_manager) { + token_manager_ = std::move(token_manager); +} + +OAuth2AccessTokenManager* ProfileOAuth2TokenService::GetAccessTokenManager() { + return token_manager_.get(); +} + +void ProfileOAuth2TokenService::OnRefreshTokenAvailable( + const CoreAccountId& account_id) { + // Check if the newly-updated token is valid (invalid tokens are inserted when + // the user signs out on the web with DICE enabled). + bool is_valid = true; + GoogleServiceAuthError token_error = GetAuthError(account_id); + if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_CLIENT)) { + is_valid = false; + } + + CancelRequestsForAccount(account_id); + ClearCacheForAccount(account_id); + + signin_metrics::RecordRefreshTokenUpdatedFromSource( + is_valid, update_refresh_token_source_); + + std::string source_string = SourceToString(update_refresh_token_source_); + if (on_refresh_token_available_callback_) + on_refresh_token_available_callback_.Run(account_id, is_valid, + source_string); +} + +void ProfileOAuth2TokenService::OnRefreshTokenRevoked( + const CoreAccountId& account_id) { + // If this was the last token, recreate the device ID. + RecreateDeviceIdIfNeeded(); + + CancelRequestsForAccount(account_id); + ClearCacheForAccount(account_id); + + signin_metrics::RecordRefreshTokenRevokedFromSource( + update_refresh_token_source_); + std::string source_string = SourceToString(update_refresh_token_source_); + if (on_refresh_token_revoked_callback_) + on_refresh_token_revoked_callback_.Run(account_id, source_string); +} + +void ProfileOAuth2TokenService::OnRefreshTokensLoaded() { + all_credentials_loaded_ = true; + + DCHECK_NE(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, + GetDelegate()->load_credentials_state()); + DCHECK_NE(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, + GetDelegate()->load_credentials_state()); + + // Reset the state for update refresh token operations to Unknown as this + // was the original state before LoadCredentials was called. + update_refresh_token_source_ = SourceForRefreshTokenOperation::kUnknown; + + // Ensure the device ID is not empty, and recreate it if all tokens were + // cleared during the loading process. + RecreateDeviceIdIfNeeded(); +} + +void ProfileOAuth2TokenService::ClearCache() { + token_manager_->ClearCache(); +} + +void ProfileOAuth2TokenService::ClearCacheForAccount( + const CoreAccountId& account_id) { + token_manager_->ClearCacheForAccount(account_id); +} + +void ProfileOAuth2TokenService::CancelAllRequests() { + token_manager_->CancelAllRequests(); +} + +void ProfileOAuth2TokenService::CancelRequestsForAccount( + const CoreAccountId& account_id) { + token_manager_->CancelRequestsForAccount(account_id); +} + +bool ProfileOAuth2TokenService::HasLoadCredentialsFinishedWithNoErrors() { + switch (GetDelegate()->load_credentials_state()) { + case signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED: + case signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS: + // LoadCredentials has not finished. + return false; + case signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED: + case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: + case signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS: + case signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS: + // LoadCredentials finished, but with errors + return false; + case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: + case signin::LoadCredentialsState:: + LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT: + // Load credentials finished with success. + return true; + } +} + +void ProfileOAuth2TokenService::RecreateDeviceIdIfNeeded() { +// On ChromeOS the device ID is not managed by the token service. +#if !defined(OS_CHROMEOS) + if (AreAllCredentialsLoaded() && HasLoadCredentialsFinishedWithNoErrors() && + GetAccounts().empty()) { + signin::RecreateSigninScopedDeviceId(user_prefs_); + } +#endif +} diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h new file mode 100644 index 00000000000..44a08697caa --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service.h @@ -0,0 +1,320 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "build/buildflag.h" +#include "components/signin/public/base/signin_buildflags.h" +#include "components/signin/public/base/signin_metrics.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +#include "net/base/backoff_entry.h" + +namespace signin { +class IdentityManager; +} + +class PrefService; +class PrefRegistrySimple; +class OAuth2AccessTokenConsumer; +class ProfileOAuth2TokenServiceDelegate; + +// ProfileOAuth2TokenService is a KeyedService that retrieves +// OAuth2 access tokens for a given set of scopes using the OAuth2 login +// refresh tokens. +// +// To use this service, call StartRequest() with a given set of scopes and a +// consumer of the request results. The consumer is required to outlive the +// request. The request can be deleted. The consumer may be called back +// asynchronously with the fetch results. +// +// - If the consumer is not called back before the request is deleted, it will +// never be called back. +// Note in this case, the actual network requests are not canceled and the +// cache will be populated with the fetched results; it is just the consumer +// callback that is aborted. +// +// - Otherwise the consumer will be called back with the request and the fetch +// results. +// +// The caller of StartRequest() owns the returned request and is responsible to +// delete the request even once the callback has been invoked. +// +// Note: after StartRequest returns, in-flight requests will continue +// even if the TokenService refresh token that was used to initiate +// the request changes or is cleared. When the request completes, +// Consumer::OnGetTokenSuccess will be invoked, but the access token +// won't be cached. +// +// Note: requests should be started from the UI thread. +class ProfileOAuth2TokenService : public OAuth2AccessTokenManager::Delegate, + public OAuth2TokenServiceObserver { + public: + typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */, + bool /* is_refresh_token_valid */, + const std::string& /* source */)> + RefreshTokenAvailableFromSourceCallback; + typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */, + const std::string& /* source */)> + RefreshTokenRevokedFromSourceCallback; + + ProfileOAuth2TokenService( + PrefService* user_prefs, + std::unique_ptr<ProfileOAuth2TokenServiceDelegate> delegate); + ~ProfileOAuth2TokenService() override; + + // Overridden from OAuth2AccessTokenManager::Delegate. + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) override; + bool HasRefreshToken(const CoreAccountId& account_id) const override; + bool FixRequestErrorIfPossible() override; + scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() + const override; + void OnAccessTokenInvalidated(const CoreAccountId& account_id, + const std::string& client_id, + const std::set<std::string>& scopes, + const std::string& access_token) override; + void OnAccessTokenFetched(const CoreAccountId& account_id, + const GoogleServiceAuthError& error) override; + + // Registers per-profile prefs. + static void RegisterProfilePrefs(PrefRegistrySimple* registry); + + ProfileOAuth2TokenServiceDelegate* GetDelegate(); + const ProfileOAuth2TokenServiceDelegate* GetDelegate() const; + + // Add or remove observers of this token service. + void AddObserver(OAuth2TokenServiceObserver* observer); + void RemoveObserver(OAuth2TokenServiceObserver* observer); + + // Add or remove observers of access token manager. + void AddAccessTokenDiagnosticsObserver( + OAuth2AccessTokenManager::DiagnosticsObserver* observer); + void RemoveAccessTokenDiagnosticsObserver( + OAuth2AccessTokenManager::DiagnosticsObserver* observer); + + // Checks in the cache for a valid access token for a specified |account_id| + // and |scopes|, and if not found starts a request for an OAuth2 access token + // using the OAuth2 refresh token maintained by this instance for that + // |account_id|. The caller owns the returned Request. + // |scopes| is the set of scopes to get an access token for, |consumer| is + // the object that will be called back with results if the returned request + // is not deleted. + std::unique_ptr<OAuth2AccessTokenManager::Request> StartRequest( + const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer); + + // Try to get refresh token from delegate. If it is accessible (i.e. not + // empty), return it directly, otherwise start request to get access token. + // Used for getting tokens to send to Gaia Multilogin endpoint. + std::unique_ptr<OAuth2AccessTokenManager::Request> StartRequestForMultilogin( + const CoreAccountId& account_id, + OAuth2AccessTokenManager::Consumer* consumer); + + // This method does the same as |StartRequest| except it uses |client_id| and + // |client_secret| to identify OAuth client app instead of using + // Chrome's default values. + std::unique_ptr<OAuth2AccessTokenManager::Request> StartRequestForClient( + const CoreAccountId& account_id, + const std::string& client_id, + const std::string& client_secret, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer); + + // This method does the same as |StartRequest| except it uses the + // URLLoaderFactory given by |url_loader_factory| instead of using the one + // returned by Delegate::GetURLLoaderFactory(). + std::unique_ptr<OAuth2AccessTokenManager::Request> StartRequestWithContext( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const OAuth2AccessTokenManager::ScopeSet& scopes, + OAuth2AccessTokenManager::Consumer* consumer); + + // Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as + // invalid. This should be done if the token was received from this class, + // but was not accepted by the server (e.g., the server returned + // 401 Unauthorized). The token will be removed from the cache for the given + // scopes. + void InvalidateAccessToken(const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes, + const std::string& access_token); + + // Removes token from cache (if it is cached) and calls + // InvalidateTokenForMultilogin method of the delegate. This should be done if + // the token was received from this class, but was not accepted by the server + // (e.g., the server returned 401 Unauthorized). + virtual void InvalidateTokenForMultilogin(const CoreAccountId& failed_account, + const std::string& token); + + // If set, this callback will be invoked when a new refresh token is + // available. Contains diagnostic information about the source of the update + // credentials operation. + void SetRefreshTokenAvailableFromSourceCallback( + RefreshTokenAvailableFromSourceCallback callback); + + // If set, this callback will be invoked when a refresh token is revoked. + // Contains diagnostic information about the source that initiated the + // revocation operation. + void SetRefreshTokenRevokedFromSourceCallback( + RefreshTokenRevokedFromSourceCallback callback); + + void Shutdown(); + + // Loads credentials from a backing persistent store to make them available + // after service is used between profile restarts. + // + // The primary account is specified with the |primary_account_id| argument. + // For a regular profile, the primary account id comes from + // PrimaryAccountManager. + // For a supervised user, the id comes from SupervisedUserService. + void LoadCredentials(const CoreAccountId& primary_account_id); + + // Returns true if LoadCredentials finished with no errors. + bool HasLoadCredentialsFinishedWithNoErrors(); + + // Updates a |refresh_token| for an |account_id|. Credentials are persisted, + // and available through |LoadCredentials| after service is restarted. + void UpdateCredentials( + const CoreAccountId& account_id, + const std::string& refresh_token, + signin_metrics::SourceForRefreshTokenOperation source = + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + + void RevokeCredentials( + const CoreAccountId& account_id, + signin_metrics::SourceForRefreshTokenOperation source = + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + + // Revokes all credentials. + void RevokeAllCredentials( + signin_metrics::SourceForRefreshTokenOperation source = + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + + // Returns a pointer to its instance of net::BackoffEntry or nullptr if there + // is no such instance. + const net::BackoffEntry* GetDelegateBackoffEntry(); + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + // Removes the credentials associated to account_id from the internal storage, + // and moves them to |to_service|. The credentials are not revoked on the + // server, but the OnRefreshTokenRevoked() notification is sent to the + // observers. + void ExtractCredentials(ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id); +#endif + + // Returns true iff all credentials have been loaded from disk. + bool AreAllCredentialsLoaded() const; + + void set_all_credentials_loaded_for_testing(bool loaded) { + all_credentials_loaded_ = loaded; + } + + // Lists account IDs of all accounts with a refresh token maintained by this + // instance. + // Note: For each account returned by |GetAccounts|, |RefreshTokenIsAvailable| + // will return true. + // Note: If tokens have not been fully loaded yet, an empty list is returned. + std::vector<CoreAccountId> GetAccounts() const; + + // Returns true if a refresh token exists for |account_id|. If false, calls to + // |StartRequest| will result in a Consumer::OnGetTokenFailure callback. + // Note: This will return |true| if and only if |account_id| is contained in + // the list returned by |GetAccounts|. + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const; + + // Returns true if a refresh token exists for |account_id| and it is in a + // persistent error state. + bool RefreshTokenHasError(const CoreAccountId& account_id) const; + + // Returns the auth error associated with |account_id|. Only persistent errors + // will be returned. + GoogleServiceAuthError GetAuthError(const CoreAccountId& account_id) const; + + // Exposes the ability to update auth errors to tests. + void UpdateAuthErrorForTesting(const CoreAccountId& account_id, + const GoogleServiceAuthError& error); + + int GetTokenCacheCountForTesting(); + + void set_max_authorization_token_fetch_retries_for_testing(int max_retries); + + // Returns the current number of pending fetchers matching given params. + size_t GetNumPendingRequestsForTesting( + const std::string& client_id, + const CoreAccountId& account_id, + const OAuth2AccessTokenManager::ScopeSet& scopes) const; + + // Override |token_manager_| for testing. + void OverrideAccessTokenManagerForTesting( + std::unique_ptr<OAuth2AccessTokenManager> token_manager); + + protected: + OAuth2AccessTokenManager* GetAccessTokenManager(); + + private: + friend class signin::IdentityManager; + + // OAuth2TokenServiceObserver implementation. + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override; + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override; + void OnRefreshTokensLoaded() override; + + // Clears the internal token cache. + void ClearCache(); + + // Clears all of the tokens belonging to |account_id| from the internal token + // cache. It does not matter what other parameters, like |client_id| were + // used to request the tokens. + void ClearCacheForAccount(const CoreAccountId& account_id); + + // Cancels all requests that are currently in progress. + void CancelAllRequests(); + + // Cancels all requests related to a given |account_id|. + void CancelRequestsForAccount(const CoreAccountId& account_id); + + // Creates a new device ID if there are no accounts, or if the current device + // ID is empty. + void RecreateDeviceIdIfNeeded(); + + PrefService* user_prefs_; + + std::unique_ptr<ProfileOAuth2TokenServiceDelegate> delegate_; + + // Whether all credentials have been loaded. + bool all_credentials_loaded_; + + std::unique_ptr<OAuth2AccessTokenManager> token_manager_; + + // Callbacks to invoke, if set, for refresh token-related events. + RefreshTokenAvailableFromSourceCallback on_refresh_token_available_callback_; + RefreshTokenRevokedFromSourceCallback on_refresh_token_revoked_callback_; + + signin_metrics::SourceForRefreshTokenOperation update_refresh_token_source_ = + signin_metrics::SourceForRefreshTokenOperation::kUnknown; + + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest, UpdateClearsCache); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest, CancelAllRequests); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest, + CancelRequestsForAccount); + + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService); +}; + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_H_ 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 new file mode 100644 index 00000000000..7c95f143435 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h" + +#include <string> +#include <utility> + +#include "components/prefs/pref_service.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/device_id_helper.h" +#include "components/signin/public/base/signin_client.h" + +#if defined(OS_ANDROID) +#include "components/signin/internal/identity_manager/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" +#endif + +#if defined(OS_CHROMEOS) +#include "chromeos/components/account_manager/account_manager.h" +#include "chromeos/constants/chromeos_switches.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h" +#include "components/user_manager/user_manager.h" +#endif // defined(OS_CHROMEOS) + +#if defined(OS_IOS) +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h" +#include "components/signin/public/identity_manager/ios/device_accounts_provider.h" +#endif + +namespace { + +#if defined(OS_ANDROID) +std::unique_ptr<OAuth2TokenServiceDelegateAndroid> CreateAndroidOAuthDelegate( + AccountTrackerService* account_tracker_service) { + return std::make_unique<OAuth2TokenServiceDelegateAndroid>( + account_tracker_service); +} +#elif defined(OS_IOS) +std::unique_ptr<ProfileOAuth2TokenServiceIOSDelegate> CreateIOSOAuthDelegate( + SigninClient* signin_client, + std::unique_ptr<DeviceAccountsProvider> device_accounts_provider, + AccountTrackerService* account_tracker_service) { + return std::make_unique<ProfileOAuth2TokenServiceIOSDelegate>( + signin_client, std::move(device_accounts_provider), + account_tracker_service); +} +#else // !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(OS_CHROMEOS) +std::unique_ptr<signin::ProfileOAuth2TokenServiceDelegateChromeOS> +CreateCrOsOAuthDelegate( + AccountTrackerService* account_tracker_service, + network::NetworkConnectionTracker* network_connection_tracker, + chromeos::AccountManager* account_manager, + bool is_regular_profile) { + DCHECK(account_manager); + return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>( + account_tracker_service, network_connection_tracker, account_manager, + is_regular_profile); +} +#endif // defined(OS_CHROMEOS) + +// Supervised users cannot revoke credentials. +bool CanRevokeCredentials() { +#if defined(OS_CHROMEOS) + // UserManager may not exist in unit_tests. + if (user_manager::UserManager::IsInitialized() && + user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) { + // Don't allow revoking credentials for Chrome OS supervised users. + // See http://crbug.com/332032 + LOG(ERROR) << "Attempt to revoke supervised user refresh " + << "token detected, ignoring."; + return false; + } +#endif + + return true; +} + +std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate> +CreateMutableProfileOAuthDelegate( + AccountTrackerService* account_tracker_service, + signin::AccountConsistencyMethod account_consistency, + bool delete_signin_cookies_on_exit, + scoped_refptr<TokenWebData> token_web_data, + SigninClient* signin_client, +#if defined(OS_WIN) + MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorCallback + reauth_callback, +#endif + network::NetworkConnectionTracker* network_connection_tracker) { + // When signin cookies are cleared on exit and Dice is enabled, all tokens + // should also be cleared. + bool revoke_all_tokens_on_load = + (account_consistency == signin::AccountConsistencyMethod::kDice) && + delete_signin_cookies_on_exit; + + return std::make_unique<MutableProfileOAuth2TokenServiceDelegate>( + signin_client, account_tracker_service, network_connection_tracker, + token_web_data, account_consistency, revoke_all_tokens_on_load, + CanRevokeCredentials(), +#if defined(OS_WIN) + reauth_callback +#else + MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorCallback() +#endif // defined(OS_WIN) + ); +} +#endif // defined(OS_ANDROID) + +std::unique_ptr<ProfileOAuth2TokenServiceDelegate> +CreateOAuth2TokenServiceDelegate( + AccountTrackerService* account_tracker_service, + signin::AccountConsistencyMethod account_consistency, + SigninClient* signin_client, +#if defined(OS_CHROMEOS) + chromeos::AccountManager* account_manager, + bool is_regular_profile, +#endif +#if !defined(OS_ANDROID) + bool delete_signin_cookies_on_exit, + scoped_refptr<TokenWebData> token_web_data, +#endif +#if defined(OS_IOS) + std::unique_ptr<DeviceAccountsProvider> device_accounts_provider, +#endif +#if defined(OS_WIN) + MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorCallback + reauth_callback, +#endif + network::NetworkConnectionTracker* network_connection_tracker) { +#if defined(OS_ANDROID) + return CreateAndroidOAuthDelegate(account_tracker_service); +#elif defined(OS_IOS) + return CreateIOSOAuthDelegate(signin_client, + std::move(device_accounts_provider), + account_tracker_service); +#else // !defined(OS_ANDROID) && !defined(OS_IOS) +#if defined(OS_CHROMEOS) + if (chromeos::switches::IsAccountManagerEnabled()) { + return CreateCrOsOAuthDelegate(account_tracker_service, + network_connection_tracker, account_manager, + is_regular_profile); + } +#endif // defined(OS_CHROMEOS) + // Fall back to |MutableProfileOAuth2TokenServiceDelegate|: + // 1. On all platforms other than Android and Chrome OS. + // 2. On Chrome OS, if Account Manager has not been switched on yet + // (chromeos::switches::IsAccountManagerEnabled). + return CreateMutableProfileOAuthDelegate( + account_tracker_service, account_consistency, + delete_signin_cookies_on_exit, token_web_data, signin_client, +#if defined(OS_WIN) + reauth_callback, +#endif + network_connection_tracker); + +#endif // defined(OS_ANDROID) +} + +} // namespace + +std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( + PrefService* pref_service, + AccountTrackerService* account_tracker_service, + network::NetworkConnectionTracker* network_connection_tracker, + signin::AccountConsistencyMethod account_consistency, +#if defined(OS_CHROMEOS) + chromeos::AccountManager* account_manager, + bool is_regular_profile, +#endif +#if !defined(OS_ANDROID) + bool delete_signin_cookies_on_exit, + scoped_refptr<TokenWebData> token_web_data, +#endif +#if defined(OS_IOS) + std::unique_ptr<DeviceAccountsProvider> device_accounts_provider, +#endif +#if defined(OS_WIN) + MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorCallback + reauth_callback, +#endif + SigninClient* signin_client) { +// On ChromeOS the device ID is not managed by the token service. +#if !defined(OS_CHROMEOS) + // Ensure the device ID is not empty. This is important for Dice, because the + // device ID is needed on the network thread, but can only be generated on the + // main thread. + std::string device_id = signin::GetSigninScopedDeviceId(pref_service); + DCHECK(!device_id.empty()); +#endif + + return std::make_unique<ProfileOAuth2TokenService>( + pref_service, + CreateOAuth2TokenServiceDelegate( + account_tracker_service, account_consistency, signin_client, +#if defined(OS_CHROMEOS) + account_manager, is_regular_profile, +#endif +#if !defined(OS_ANDROID) + delete_signin_cookies_on_exit, token_web_data, +#endif +#if defined(OS_IOS) + std::move(device_accounts_provider), +#endif +#if defined(OS_WIN) + reauth_callback, +#endif + network_connection_tracker)); +} diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h new file mode 100644 index 00000000000..c39bf35241e --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h @@ -0,0 +1,68 @@ +// 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_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_BUILDER_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_BUILDER_H_ + +#include <memory> + +#include "build/build_config.h" + +#if !defined(OS_ANDROID) +#include "base/memory/scoped_refptr.h" +#endif + +#if defined(OS_WIN) +#include "components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h" +#endif + +class AccountTrackerService; +class PrefService; +class ProfileOAuth2TokenService; +class SigninClient; + +#if defined(OS_IOS) +class DeviceAccountsProvider; +#endif + +namespace signin { +enum class AccountConsistencyMethod; +} + +namespace network { +class NetworkConnectionTracker; +} + +#if !defined(OS_ANDROID) +class TokenWebData; +#endif + +#if defined(OS_CHROMEOS) +namespace chromeos { +class AccountManager; +} +#endif + +std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( + PrefService* pref_service, + AccountTrackerService* account_tracker_service, + network::NetworkConnectionTracker* network_connection_tracker, + signin::AccountConsistencyMethod account_consistency, +#if defined(OS_CHROMEOS) + chromeos::AccountManager* account_manager, + bool is_regular_profile, +#endif +#if !defined(OS_ANDROID) + bool delete_signin_cookies_on_exit, + scoped_refptr<TokenWebData> token_web_data, +#endif +#if defined(OS_IOS) + std::unique_ptr<DeviceAccountsProvider> device_accounts_provider, +#endif +#if defined(OS_WIN) + MutableProfileOAuth2TokenServiceDelegate::FixRequestErrorCallback + reauth_callback, +#endif + SigninClient* signin_client); +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_BUILDER_H_ diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc new file mode 100644 index 00000000000..15c4384edfe --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.cc @@ -0,0 +1,136 @@ +// Copyright 2015 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/internal/identity_manager/profile_oauth2_token_service_delegate.h" + +#include "google_apis/gaia/oauth2_access_token_consumer.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +ProfileOAuth2TokenServiceDelegate::ScopedBatchChange::ScopedBatchChange( + ProfileOAuth2TokenServiceDelegate* delegate) + : delegate_(delegate) { + DCHECK(delegate_); + delegate_->StartBatchChanges(); +} + +ProfileOAuth2TokenServiceDelegate::ScopedBatchChange::~ScopedBatchChange() { + delegate_->EndBatchChanges(); +} + +ProfileOAuth2TokenServiceDelegate::ProfileOAuth2TokenServiceDelegate() + : batch_change_depth_(0) {} + +ProfileOAuth2TokenServiceDelegate::~ProfileOAuth2TokenServiceDelegate() = + default; + +bool ProfileOAuth2TokenServiceDelegate::ValidateAccountId( + const CoreAccountId& account_id) const { + bool valid = !account_id.empty(); + + // If the account is given as an email, make sure its a canonical email. + // Note that some tests don't use email strings as account id, and after + // the gaia id migration it won't be an email. So only check for + // canonicalization if the account_id is suspected to be an email. + if (account_id.id.find('@') != std::string::npos && + gaia::CanonicalizeEmail(account_id.id) != account_id.id) { + valid = false; + } + + DCHECK(valid); + return valid; +} + +void ProfileOAuth2TokenServiceDelegate::AddObserver( + OAuth2TokenServiceObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ProfileOAuth2TokenServiceDelegate::RemoveObserver( + OAuth2TokenServiceObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void ProfileOAuth2TokenServiceDelegate::StartBatchChanges() { + ++batch_change_depth_; +} + +void ProfileOAuth2TokenServiceDelegate::EndBatchChanges() { + --batch_change_depth_; + DCHECK_LE(0, batch_change_depth_); + if (batch_change_depth_ == 0) { + for (auto& observer : observer_list_) + observer.OnEndBatchChanges(); + } +} + +void ProfileOAuth2TokenServiceDelegate::FireRefreshTokenAvailable( + const CoreAccountId& account_id) { + DCHECK(!account_id.empty()); + for (auto& observer : observer_list_) + observer.OnRefreshTokenAvailable(account_id); +} + +void ProfileOAuth2TokenServiceDelegate::FireRefreshTokenRevoked( + const CoreAccountId& account_id) { + DCHECK(!account_id.empty()); + for (auto& observer : observer_list_) + observer.OnRefreshTokenRevoked(account_id); +} + +void ProfileOAuth2TokenServiceDelegate::FireRefreshTokensLoaded() { + for (auto& observer : observer_list_) + observer.OnRefreshTokensLoaded(); +} + +void ProfileOAuth2TokenServiceDelegate::FireAuthErrorChanged( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + DCHECK(!account_id.empty()); + for (auto& observer : observer_list_) + observer.OnAuthErrorChanged(account_id, error); +} + +std::string ProfileOAuth2TokenServiceDelegate::GetTokenForMultilogin( + const CoreAccountId& account_id) const { + return std::string(); +} + +scoped_refptr<network::SharedURLLoaderFactory> +ProfileOAuth2TokenServiceDelegate::GetURLLoaderFactory() const { + return nullptr; +} + +GoogleServiceAuthError ProfileOAuth2TokenServiceDelegate::GetAuthError( + const CoreAccountId& account_id) const { + return GoogleServiceAuthError::AuthErrorNone(); +} + +std::vector<CoreAccountId> ProfileOAuth2TokenServiceDelegate::GetAccounts() + const { + return std::vector<CoreAccountId>(); +} + +const net::BackoffEntry* ProfileOAuth2TokenServiceDelegate::BackoffEntry() + const { + return nullptr; +} + +void ProfileOAuth2TokenServiceDelegate::LoadCredentials( + const CoreAccountId& primary_account_id) { + NOTREACHED() + << "ProfileOAuth2TokenServiceDelegate does not load credentials. " + "Subclasses that need to load credentials must provide " + "an implemenation of this method"; +} + +void ProfileOAuth2TokenServiceDelegate::ExtractCredentials( + ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id) { + NOTREACHED(); +} + +bool ProfileOAuth2TokenServiceDelegate::FixRequestErrorIfPossible() { + return false; +} diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h new file mode 100644 index 00000000000..3e7738c8bfd --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h @@ -0,0 +1,188 @@ +// Copyright 2015 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_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "build/build_config.h" +#include "components/signin/public/identity_manager/load_credentials_state.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "net/base/backoff_entry.h" + +namespace network { +class SharedURLLoaderFactory; +} + +class OAuth2AccessTokenFetcher; +class OAuth2AccessTokenConsumer; +class OAuth2TokenServiceObserver; +class ProfileOAuth2TokenService; + +// Abstract base class to fetch and maintain refresh tokens from various +// entities. Concrete subclasses should implement RefreshTokenIsAvailable and +// CreateAccessTokenFetcher properly. +class ProfileOAuth2TokenServiceDelegate { + public: + ProfileOAuth2TokenServiceDelegate(); + virtual ~ProfileOAuth2TokenServiceDelegate(); + + virtual std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) WARN_UNUSED_RESULT = 0; + + // Returns |true| if a refresh token is available for |account_id|, and + // |false| otherwise. + // Note: Implementations must make sure that |RefreshTokenIsAvailable| returns + // |true| if and only if |account_id| is contained in the list of accounts + // returned by |GetAccounts|. + virtual bool RefreshTokenIsAvailable( + const CoreAccountId& account_id) const = 0; + virtual GoogleServiceAuthError GetAuthError( + const CoreAccountId& account_id) const; + virtual void UpdateAuthError(const CoreAccountId& account_id, + const GoogleServiceAuthError& error) {} + + // Returns a list of accounts for which a refresh token is maintained by + // |this| instance. + // Note: If tokens have not been fully loaded yet, an empty list is returned. + // Also, see |RefreshTokenIsAvailable|. + virtual std::vector<CoreAccountId> GetAccounts() const; + virtual void RevokeAllCredentials() {} + + virtual void OnAccessTokenInvalidated(const CoreAccountId& account_id, + const std::string& client_id, + const std::set<std::string>& scopes, + const std::string& access_token) {} + + // If refresh token is accessible (on Desktop) sets error for it to + // INVALID_GAIA_CREDENTIALS and notifies the observers. Otherwise + // does nothing. + virtual void InvalidateTokenForMultilogin( + const CoreAccountId& failed_account) {} + + virtual void Shutdown() {} + virtual void UpdateCredentials(const CoreAccountId& account_id, + const std::string& refresh_token) {} + virtual void RevokeCredentials(const CoreAccountId& account_id) {} + virtual scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() + const; + + // Returns refresh token if the platform allows it (on Desktop) and if it is + // available and doesn't have error. Otherwise returns empty string (for iOS + // and Android). + virtual std::string GetTokenForMultilogin( + const CoreAccountId& account_id) const; + + bool ValidateAccountId(const CoreAccountId& account_id) const; + + // Add or remove observers of this token service. + void AddObserver(OAuth2TokenServiceObserver* observer); + void RemoveObserver(OAuth2TokenServiceObserver* observer); + + // Returns a pointer to its instance of net::BackoffEntry if it has one, or + // a nullptr otherwise. + virtual const net::BackoffEntry* BackoffEntry() const; + + // ----------------------------------------------------------------------- + // Methods that are only used by ProfileOAuth2TokenService. + // ----------------------------------------------------------------------- + + // Loads the credentials from disk. Called only once when the token service + // is initialized. Default implementation is NOTREACHED - subsclasses that + // are used by the ProfileOAuth2TokenService must provide an implementation + // for this method. + virtual void LoadCredentials(const CoreAccountId& primary_account_id); + + // Returns the state of the load credentials operation. + signin::LoadCredentialsState load_credentials_state() const { + return load_credentials_state_; + } + + // Removes the credentials associated to account_id from the internal storage, + // and moves them to |to_service|. The credentials are not revoked on the + // server, but the OnRefreshTokenRevoked() notification is sent to the + // observers. + virtual void ExtractCredentials(ProfileOAuth2TokenService* to_service, + const CoreAccountId& account_id); + + // Attempts to fix the error if possible. Returns true if the error was fixed + // and false otherwise. + virtual bool FixRequestErrorIfPossible(); + +#if defined(OS_IOS) + // Triggers platform specific implementation for iOS to reload all accounts + // from system. + virtual void ReloadAllAccountsFromSystem() {} + + // Triggers platform specific implementation for iOS to add a given account + // to the token service from a system account. + virtual void ReloadAccountFromSystem(const CoreAccountId& account_id) {} +#endif + +#if defined(OS_ANDROID) + // Triggers platform specific implementation for Android to reload accounts + // from system. + virtual void ReloadAccountsFromSystem( + const CoreAccountId& primary_account_id) {} +#endif + + // ----------------------------------------------------------------------- + // End of methods that are only used by ProfileOAuth2TokenService + // ----------------------------------------------------------------------- + + protected: + void set_load_credentials_state(signin::LoadCredentialsState state) { + load_credentials_state_ = state; + } + + // Called by subclasses to notify observers. + void FireRefreshTokenAvailable(const CoreAccountId& account_id); + void FireRefreshTokenRevoked(const CoreAccountId& account_id); + // FireRefreshTokensLoaded is virtual and overridden in android implementation + // where additional actions are required. + virtual void FireRefreshTokensLoaded(); + void FireAuthErrorChanged(const CoreAccountId& account_id, + const GoogleServiceAuthError& error); + + // Helper class to scope batch changes. + class ScopedBatchChange { + public: + explicit ScopedBatchChange(ProfileOAuth2TokenServiceDelegate* delegate); + ~ScopedBatchChange(); + + private: + ProfileOAuth2TokenServiceDelegate* delegate_; // Weak. + DISALLOW_COPY_AND_ASSIGN(ScopedBatchChange); + }; + + private: + // List of observers to notify when refresh token availability changes. + // Makes sure list is empty on destruction. + base::ObserverList<OAuth2TokenServiceObserver, true>::Unchecked + observer_list_; + + // The state of the load credentials operation. + signin::LoadCredentialsState load_credentials_state_ = + signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED; + + void StartBatchChanges(); + void EndBatchChanges(); + + // The depth of batch changes. + int batch_change_depth_; + + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceDelegate); +}; + +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_H_ diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc index 00d05e5fabb..3a064c68e01 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.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/core/browser/profile_oauth2_token_service_delegate_chromeos.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h" #include <string> #include <utility> @@ -11,7 +11,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/stl_util.h" -#include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" #include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h" #include "net/base/backoff_entry.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -42,17 +42,17 @@ const net::BackoffEntry::Policy kBackoffPolicy = { // and non-Gaia accounts. Non-Gaia accounts will be filtered out. // |account_keys| is the set of accounts that need to be translated. // |account_tracker_service| is an unowned pointer. -std::vector<std::string> GetOAuthAccountIdsFromAccountKeys( +std::vector<CoreAccountId> GetOAuthAccountIdsFromAccountKeys( const std::set<chromeos::AccountManager::AccountKey>& account_keys, const AccountTrackerService* const account_tracker_service) { - std::vector<std::string> accounts; + std::vector<CoreAccountId> accounts; for (auto& account_key : account_keys) { if (account_key.account_type != chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA) { continue; } - std::string account_id = + CoreAccountId account_id = account_tracker_service ->FindAccountInfoByGaiaId(account_key.id /* gaia_id */) .account_id; @@ -87,13 +87,15 @@ ProfileOAuth2TokenServiceDelegateChromeOS:: network_connection_tracker_->RemoveNetworkConnectionObserver(this); } -OAuth2AccessTokenFetcher* +std::unique_ptr<OAuth2AccessTokenFetcher> ProfileOAuth2TokenServiceDelegateChromeOS::CreateAccessTokenFetcher( - const std::string& account_id, + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state()); + DCHECK_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + load_credentials_state()); ValidateAccountId(account_id); @@ -105,7 +107,7 @@ ProfileOAuth2TokenServiceDelegateChromeOS::CreateAccessTokenFetcher( VLOG(1) << "Request for token has been rejected due to persistent error #" << it->second.last_auth_error.state(); // |OAuth2TokenService| will manage the lifetime of this pointer. - return new OAuth2AccessTokenFetcherImmediateError( + return std::make_unique<OAuth2AccessTokenFetcherImmediateError>( consumer, it->second.last_auth_error); } // Or when we need to backoff. @@ -113,18 +115,16 @@ ProfileOAuth2TokenServiceDelegateChromeOS::CreateAccessTokenFetcher( VLOG(1) << "Request for token has been rejected due to backoff rules from" << " previous error #" << backoff_error_.state(); // |OAuth2TokenService| will manage the lifetime of this pointer. - return new OAuth2AccessTokenFetcherImmediateError(consumer, backoff_error_); + return std::make_unique<OAuth2AccessTokenFetcherImmediateError>( + consumer, backoff_error_); } - // |OAuth2TokenService| will manage the lifetime of the released pointer. - return account_manager_ - ->CreateAccessTokenFetcher( - chromeos::AccountManager::AccountKey{ - account_tracker_service_->GetAccountInfo(account_id).gaia, - chromeos::account_manager::AccountType:: - ACCOUNT_TYPE_GAIA} /* account_key */, - url_loader_factory, consumer) - .release(); + return account_manager_->CreateAccessTokenFetcher( + chromeos::AccountManager::AccountKey{ + account_tracker_service_->GetAccountInfo(account_id).gaia, + chromeos::account_manager::AccountType:: + ACCOUNT_TYPE_GAIA} /* account_key */, + url_loader_factory, consumer); } // Note: This method should use the same logic for filtering accounts as @@ -132,20 +132,21 @@ ProfileOAuth2TokenServiceDelegateChromeOS::CreateAccessTokenFetcher( // both |GetAccounts| and |RefreshTokenIsAvailable| use // |GetOAuthAccountIdsFromAccountKeys|. bool ProfileOAuth2TokenServiceDelegateChromeOS::RefreshTokenIsAvailable( - const std::string& account_id) const { - if (load_credentials_state() != LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) { + const CoreAccountId& account_id) const { + if (load_credentials_state() != + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS) { return false; } // We intentionally do NOT check if the refresh token associated with // |account_id| is valid or not. See crbug.com/919793 for details. - return base::ContainsValue(GetOAuthAccountIdsFromAccountKeys( - account_keys_, account_tracker_service_), - account_id); + return base::Contains(GetOAuthAccountIdsFromAccountKeys( + account_keys_, account_tracker_service_), + account_id); } void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateAuthError( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -158,6 +159,8 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateAuthError( auto it = errors_.find(account_id); if (it != errors_.end()) { + if (error == it->second.last_auth_error) + return; // Update the existing error. if (error.state() == GoogleServiceAuthError::NONE) errors_.erase(it); @@ -172,7 +175,7 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateAuthError( } GoogleServiceAuthError ProfileOAuth2TokenServiceDelegateChromeOS::GetAuthError( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto it = errors_.find(account_id); if (it != errors_.end()) { return it->second.last_auth_error; @@ -185,8 +188,8 @@ GoogleServiceAuthError ProfileOAuth2TokenServiceDelegateChromeOS::GetAuthError( // |RefreshTokenIsAvailable|. See crbug.com/919793 for details. At the time of // writing, both |GetAccounts| and |RefreshTokenIsAvailable| use // |GetOAuthAccountIdsFromAccountKeys|. -std::vector<std::string> -ProfileOAuth2TokenServiceDelegateChromeOS::GetAccounts() { +std::vector<CoreAccountId> +ProfileOAuth2TokenServiceDelegateChromeOS::GetAccounts() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // |GetAccounts| intentionally does not care about the state of @@ -198,13 +201,15 @@ ProfileOAuth2TokenServiceDelegateChromeOS::GetAccounts() { } void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials( - const std::string& primary_account_id) { + const CoreAccountId& primary_account_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (load_credentials_state() != LOAD_CREDENTIALS_NOT_STARTED) { + if (load_credentials_state() != + signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED) { return; } - set_load_credentials_state(LOAD_CREDENTIALS_IN_PROGRESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS); if (!is_regular_profile_) { // |LoadCredentials| needs to complete successfully for a successful Profile @@ -213,7 +218,8 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials( // to them. Note: They do have access to an Account Manager instance, but // that instance is never set up (|AccountManager::Initialize|). Also, see // http://crbug.com/891818 - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); FireRefreshTokensLoaded(); return; } @@ -226,16 +232,16 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials( } void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateCredentials( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token) { // This API could have been called for upserting the Device/Primary // |account_id| or a Secondary |account_id|. // Account insertion: // Device Account insertion on Chrome OS happens as a 2 step process: - // 1. The account is inserted into SigninManager / AccountTrackerService, via - // IdentityManager, with a valid Gaia id and email but an invalid refresh - // token. + // 1. The account is inserted into PrimaryAccountManager / + // AccountTrackerService, via IdentityManager, with a valid Gaia id and email + // but an invalid refresh token. // 2. This API is called to update the aforementioned account with a valid // refresh token. // Secondary Account insertion on Chrome OS happens atomically in @@ -254,7 +260,9 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateCredentials( // Accounts) we can be sure that |account_id| is present in // |AccountTrackerService|. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state()); + DCHECK_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + load_credentials_state()); DCHECK(!account_id.empty()); DCHECK(!refresh_token.empty()); @@ -287,10 +295,12 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnGetAccounts( // This callback should only be triggered during |LoadCredentials|, which // implies that |load_credentials_state())| should in // |LOAD_CREDENTIALS_IN_PROGRESS| state. - DCHECK_EQ(LOAD_CREDENTIALS_IN_PROGRESS, load_credentials_state()); + DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS, + load_credentials_state()); - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); - // The typical order of |OAuth2TokenService::Observer| callbacks is: + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + // The typical order of |OAuth2TokenServiceObserver| callbacks is: // 1. OnRefreshTokenAvailable // 2. OnEndBatchChanges // 3. OnRefreshTokensLoaded @@ -341,7 +351,7 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnTokenUpserted( ScopedBatchChange batch(this); FireRefreshTokenAvailable(account_id); - // See |OAuth2TokenService::Observer::OnAuthErrorChanged|. + // See |OAuth2TokenServiceObserver::OnAuthErrorChanged|. // |OnAuthErrorChanged| must be always called after // |OnRefreshTokenAvailable|, when refresh token is updated. FireAuthErrorChanged(account_id, error); @@ -350,7 +360,9 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnTokenUpserted( void ProfileOAuth2TokenServiceDelegateChromeOS::OnAccountRemoved( const chromeos::AccountManager::Account& account) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, load_credentials_state()); + DCHECK_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + load_credentials_state()); auto it = account_keys_.find(account.key); if (it == account_keys_.end()) { @@ -376,7 +388,7 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnAccountRemoved( } void ProfileOAuth2TokenServiceDelegateChromeOS::RevokeCredentials( - const std::string& account_id) { + const CoreAccountId& account_id) { // Signing out of Chrome is not possible on Chrome OS. NOTREACHED(); } diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h index b515c53df60..affc6247883 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.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_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ #include <map> #include <memory> @@ -15,15 +15,14 @@ #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" #include "chromeos/components/account_manager/account_manager.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" #include "services/network/public/cpp/network_connection_tracker.h" class AccountTrackerService; namespace signin { - class ProfileOAuth2TokenServiceDelegateChromeOS - : public OAuth2TokenServiceDelegate, + : public ProfileOAuth2TokenServiceDelegate, public chromeos::AccountManager::Observer, public network::NetworkConnectionTracker::NetworkConnectionObserver { public: @@ -37,23 +36,23 @@ class ProfileOAuth2TokenServiceDelegateChromeOS bool is_regular_profile); ~ProfileOAuth2TokenServiceDelegateChromeOS() override; - // OAuth2TokenServiceDelegate overrides. - OAuth2AccessTokenFetcher* CreateAccessTokenFetcher( - const std::string& account_id, + // ProfileOAuth2TokenServiceDelegate overrides. + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) override; - bool RefreshTokenIsAvailable(const std::string& account_id) const override; - void UpdateAuthError(const std::string& account_id, + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override; + void UpdateAuthError(const CoreAccountId& account_id, const GoogleServiceAuthError& error) override; GoogleServiceAuthError GetAuthError( - const std::string& account_id) const override; - std::vector<std::string> GetAccounts() override; - void LoadCredentials(const std::string& primary_account_id) override; - void UpdateCredentials(const std::string& account_id, + const CoreAccountId& account_id) const override; + std::vector<CoreAccountId> GetAccounts() const override; + void LoadCredentials(const CoreAccountId& primary_account_id) override; + void UpdateCredentials(const CoreAccountId& account_id, const std::string& refresh_token) override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() const override; - void RevokeCredentials(const std::string& account_id) override; + void RevokeCredentials(const CoreAccountId& account_id) override; void RevokeAllCredentials() override; const net::BackoffEntry* BackoffEntry() const override; @@ -91,7 +90,7 @@ class ProfileOAuth2TokenServiceDelegateChromeOS std::set<chromeos::AccountManager::AccountKey> account_keys_; // A map from account id to the last seen error for that account. - std::map<std::string, AccountErrorStatus> errors_; + std::map<CoreAccountId, AccountErrorStatus> errors_; // Used to rate-limit token fetch requests so as to not overload the server. net::BackoffEntry backoff_entry_; @@ -108,4 +107,4 @@ class ProfileOAuth2TokenServiceDelegateChromeOS } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_CHROMEOS_H_ diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_unittest.cc index 088a552ca34..d161cb81862 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos_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/core/browser/profile_oauth2_token_service_delegate_chromeos.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h" #include <memory> #include <set> @@ -16,15 +16,16 @@ #include "base/stl_util.h" #include "base/test/scoped_task_environment.h" #include "chromeos/components/account_manager/account_manager.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/account_info.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" -#include "google_apis/gaia/oauth2_token_service.h" -#include "google_apis/gaia/oauth2_token_service_test_util.h" +#include "google_apis/gaia/oauth2_access_token_fetcher.h" +#include "google_apis/gaia/oauth2_access_token_manager_test_util.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" #include "services/network/test/test_network_connection_tracker.h" #include "testing/gtest/include/gtest/gtest.h" @@ -59,16 +60,19 @@ class AccessTokenConsumer : public OAuth2AccessTokenConsumer { DISALLOW_COPY_AND_ASSIGN(AccessTokenConsumer); }; -class TokenServiceObserver : public OAuth2TokenService::Observer { +class TestOAuth2TokenServiceObserver : public OAuth2TokenServiceObserver { public: - // |delegate| is a non-owning pointer to an |OAuth2TokenServiceDelegate| that - // MUST outlive |this| instance. - explicit TokenServiceObserver(OAuth2TokenServiceDelegate* delegate) + // |delegate| is a non-owning pointer to an + // |ProfileOAuth2TokenServiceDelegate| that MUST outlive |this| instance. + explicit TestOAuth2TokenServiceObserver( + ProfileOAuth2TokenServiceDelegate* delegate) : delegate_(delegate) { delegate_->AddObserver(this); } - ~TokenServiceObserver() override { delegate_->RemoveObserver(this); } + ~TestOAuth2TokenServiceObserver() override { + delegate_->RemoveObserver(this); + } void StartBatchChanges() { EXPECT_FALSE(is_inside_batch_); @@ -83,7 +87,7 @@ class TokenServiceObserver : public OAuth2TokenService::Observer { is_inside_batch_ = false; } - void OnRefreshTokenAvailable(const std::string& account_id) override { + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override { if (!is_inside_batch_) StartBatchChanges(); @@ -106,7 +110,7 @@ class TokenServiceObserver : public OAuth2TokenService::Observer { void OnRefreshTokensLoaded() override { refresh_tokens_loaded_ = true; } - void OnRefreshTokenRevoked(const std::string& account_id) override { + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override { if (!is_inside_batch_) StartBatchChanges(); @@ -115,12 +119,15 @@ class TokenServiceObserver : public OAuth2TokenService::Observer { batch_change_records_.rbegin()->emplace_back(account_id); } - void OnAuthErrorChanged(const std::string& account_id, + void OnAuthErrorChanged(const CoreAccountId& account_id, const GoogleServiceAuthError& auth_error) override { last_err_account_id_ = account_id; last_err_ = auth_error; + on_auth_error_changed_calls++; } + int on_auth_error_changed_calls = 0; + std::string last_err_account_id_; GoogleServiceAuthError last_err_; std::set<std::string> account_ids_; @@ -133,7 +140,7 @@ class TokenServiceObserver : public OAuth2TokenService::Observer { std::vector<std::vector<std::string>> batch_change_records_; // Non-owning pointer. - OAuth2TokenServiceDelegate* const delegate_; + ProfileOAuth2TokenServiceDelegate* const delegate_; }; } // namespace @@ -229,54 +236,51 @@ TEST_F(CrOSOAuthDelegateTest, RefreshTokensAreLoadedForNonRegularProfiles) { &account_tracker_service_, network::TestNetworkConnectionTracker::GetInstance(), &account_manager, false /* is_regular_profile */); - TokenServiceObserver observer(delegate.get()); + TestOAuth2TokenServiceObserver observer(delegate.get()); // Test that LoadCredentials works as expected. EXPECT_FALSE(observer.refresh_tokens_loaded_); delegate->LoadCredentials("" /* primary_account_id */); EXPECT_TRUE(observer.refresh_tokens_loaded_); - EXPECT_EQ(OAuth2TokenServiceDelegate::LoadCredentialsState:: - LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, delegate->load_credentials_state()); } TEST_F(CrOSOAuthDelegateTest, RefreshTokenIsAvailableReturnsTrueForValidGaiaTokens) { - EXPECT_EQ(OAuth2TokenServiceDelegate::LoadCredentialsState:: - LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, delegate_->load_credentials_state()); EXPECT_FALSE(delegate_->RefreshTokenIsAvailable(account_info_.account_id)); EXPECT_FALSE( - base::ContainsValue(delegate_->GetAccounts(), account_info_.account_id)); + base::Contains(delegate_->GetAccounts(), account_info_.account_id)); account_manager_.UpsertAccount(gaia_account_key_, kUserEmail, kGaiaToken); EXPECT_TRUE(delegate_->RefreshTokenIsAvailable(account_info_.account_id)); EXPECT_TRUE( - base::ContainsValue(delegate_->GetAccounts(), account_info_.account_id)); + base::Contains(delegate_->GetAccounts(), account_info_.account_id)); } TEST_F(CrOSOAuthDelegateTest, RefreshTokenIsAvailableReturnsTrueForInvalidGaiaTokens) { - EXPECT_EQ(OAuth2TokenServiceDelegate::LoadCredentialsState:: - LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, delegate_->load_credentials_state()); EXPECT_FALSE(delegate_->RefreshTokenIsAvailable(account_info_.account_id)); EXPECT_FALSE( - base::ContainsValue(delegate_->GetAccounts(), account_info_.account_id)); + base::Contains(delegate_->GetAccounts(), account_info_.account_id)); account_manager_.UpsertAccount(gaia_account_key_, kUserEmail, chromeos::AccountManager::kInvalidToken); EXPECT_TRUE(delegate_->RefreshTokenIsAvailable(account_info_.account_id)); EXPECT_TRUE( - base::ContainsValue(delegate_->GetAccounts(), account_info_.account_id)); + base::Contains(delegate_->GetAccounts(), account_info_.account_id)); } TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnAuthErrorChange) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); auto error = GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR); @@ -286,8 +290,33 @@ TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnAuthErrorChange) { EXPECT_EQ(error, observer.last_err_); } +TEST_F(CrOSOAuthDelegateTest, ObserversAreNotNotifiedIfErrorDidntChange) { + TestOAuth2TokenServiceObserver observer(delegate_.get()); + auto error = + GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR); + + delegate_->UpdateAuthError(account_info_.account_id, error); + EXPECT_EQ(1, observer.on_auth_error_changed_calls); + delegate_->UpdateAuthError(account_info_.account_id, error); + EXPECT_EQ(1, observer.on_auth_error_changed_calls); +} + +TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedIfErrorDidChange) { + TestOAuth2TokenServiceObserver observer(delegate_.get()); + delegate_->UpdateAuthError( + account_info_.account_id, + GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR)); + EXPECT_EQ(1, observer.on_auth_error_changed_calls); + + delegate_->UpdateAuthError( + account_info_.account_id, + GoogleServiceAuthError( + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS)); + EXPECT_EQ(2, observer.on_auth_error_changed_calls); +} + TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsInsertion) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); EXPECT_EQ(1UL, observer.account_ids_.size()); @@ -298,7 +327,7 @@ TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsInsertion) { TEST_F(CrOSOAuthDelegateTest, ObserversDoNotSeeCachedErrorsOnCredentialsUpdate) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); auto error = GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR); delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); @@ -310,7 +339,7 @@ TEST_F(CrOSOAuthDelegateTest, } TEST_F(CrOSOAuthDelegateTest, DummyTokensArePreEmptivelyRejected) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); delegate_->UpdateCredentials(account_info_.account_id, chromeos::AccountManager::kInvalidToken); @@ -328,7 +357,7 @@ TEST_F(CrOSOAuthDelegateTest, DummyTokensArePreEmptivelyRejected) { } TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsUpdate) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); EXPECT_EQ(1UL, observer.account_ids_.size()); @@ -339,7 +368,7 @@ TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnCredentialsUpdate) { TEST_F(CrOSOAuthDelegateTest, ObserversAreNotNotifiedIfCredentialsAreNotUpdated) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); observer.account_ids_.clear(); @@ -352,7 +381,7 @@ TEST_F(CrOSOAuthDelegateTest, TEST_F(CrOSOAuthDelegateTest, BatchChangeObserversAreNotifiedOnCredentialsUpdate) { - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); EXPECT_EQ(1UL, observer.batch_change_records_.size()); @@ -360,9 +389,9 @@ TEST_F(CrOSOAuthDelegateTest, EXPECT_EQ(account_info_.account_id, observer.batch_change_records_[0][0]); } -// If observers register themselves with |OAuth2TokenServiceDelegate| before -// |chromeos::AccountManager| has been initialized, they should receive all the -// accounts stored in |chromeos::AccountManager| in a single batch. +// If observers register themselves with |ProfileOAuth2TokenServiceDelegate| +// before |chromeos::AccountManager| has been initialized, they should receive +// all the accounts stored in |chromeos::AccountManager| in a single batch. TEST_F(CrOSOAuthDelegateTest, BatchChangeObserversAreNotifiedOncePerBatch) { // Setup AccountInfo account1 = CreateAccountInfoTestFixture( @@ -393,7 +422,7 @@ TEST_F(CrOSOAuthDelegateTest, BatchChangeObserversAreNotifiedOncePerBatch) { network::TestNetworkConnectionTracker::GetInstance(), &account_manager, true /* is_regular_profile */); delegate->LoadCredentials(account1.account_id /* primary_account_id */); - TokenServiceObserver observer(delegate.get()); + TestOAuth2TokenServiceObserver observer(delegate.get()); // Wait until chromeos::AccountManager is fully initialized. task_environment_.RunUntilIdle(); @@ -411,8 +440,8 @@ TEST_F(CrOSOAuthDelegateTest, BatchChangeObserversAreNotifiedOncePerBatch) { const std::vector<std::string>& first_batch = observer.batch_change_records_[0]; EXPECT_EQ(2UL, first_batch.size()); - EXPECT_TRUE(base::ContainsValue(first_batch, account1.account_id)); - EXPECT_TRUE(base::ContainsValue(first_batch, account2.account_id)); + EXPECT_TRUE(base::Contains(first_batch, account1.account_id)); + EXPECT_TRUE(base::Contains(first_batch, account2.account_id)); } TEST_F(CrOSOAuthDelegateTest, GetAccountsShouldNotReturnAdAccounts) { @@ -432,7 +461,7 @@ TEST_F(CrOSOAuthDelegateTest, GetAccountsReturnsGaiaAccounts) { account_manager_.UpsertAccount(gaia_account_key_, kUserEmail, kGaiaToken); - std::vector<std::string> accounts = delegate_->GetAccounts(); + std::vector<CoreAccountId> accounts = delegate_->GetAccounts(); EXPECT_EQ(1UL, accounts.size()); EXPECT_EQ(account_info_.account_id, accounts[0]); } @@ -445,15 +474,14 @@ TEST_F(CrOSOAuthDelegateTest, GetAccountsReturnsGaiaAccountsWithInvalidTokens) { account_manager_.UpsertAccount(gaia_account_key_, kUserEmail, chromeos::AccountManager::kInvalidToken); - std::vector<std::string> accounts = delegate_->GetAccounts(); + std::vector<CoreAccountId> accounts = delegate_->GetAccounts(); EXPECT_EQ(1UL, accounts.size()); EXPECT_EQ(account_info_.account_id, accounts[0]); } TEST_F(CrOSOAuthDelegateTest, RefreshTokenMustBeAvailableForAllAccountsReturnedByGetAccounts) { - EXPECT_EQ(OAuth2TokenServiceDelegate::LoadCredentialsState:: - LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + EXPECT_EQ(LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, delegate_->load_credentials_state()); EXPECT_TRUE(delegate_->GetAccounts().empty()); const std::string kUserEmail2 = "random-email2@example.com"; @@ -475,11 +503,11 @@ TEST_F(CrOSOAuthDelegateTest, chromeos::AccountManager::kActiveDirectoryDummyToken); // Verify. - const std::vector<std::string> accounts = delegate_->GetAccounts(); + const std::vector<CoreAccountId> accounts = delegate_->GetAccounts(); // 2 Gaia accounts should be returned. EXPECT_EQ(2UL, accounts.size()); // And |RefreshTokenIsAvailable| should return true for these accounts. - for (const std::string& account : accounts) { + for (const CoreAccountId& account : accounts) { EXPECT_TRUE(delegate_->RefreshTokenIsAvailable(account)); } } @@ -489,7 +517,7 @@ TEST_F(CrOSOAuthDelegateTest, UpdateCredentialsSucceeds) { delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); - std::vector<std::string> accounts = delegate_->GetAccounts(); + std::vector<CoreAccountId> accounts = delegate_->GetAccounts(); EXPECT_EQ(1UL, accounts.size()); EXPECT_EQ(account_info_.account_id, accounts[0]); } @@ -497,7 +525,7 @@ TEST_F(CrOSOAuthDelegateTest, UpdateCredentialsSucceeds) { TEST_F(CrOSOAuthDelegateTest, ObserversAreNotifiedOnAccountRemoval) { delegate_->UpdateCredentials(account_info_.account_id, kGaiaToken); - TokenServiceObserver observer(delegate_.get()); + TestOAuth2TokenServiceObserver observer(delegate_.get()); account_manager_.RemoveAccount(gaia_account_key_); EXPECT_EQ(1UL, observer.batch_change_records_.size()); @@ -542,10 +570,10 @@ TEST_F(CrOSOAuthDelegateTest, BackOffIsTriggerredForTransientErrors) { EXPECT_EQ(0, access_token_consumer.num_access_token_fetch_success_); EXPECT_EQ(0, access_token_consumer.num_access_token_fetch_failure_); std::vector<std::string> scopes{"scope"}; - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher = delegate_->CreateAccessTokenFetcher(account_info_.account_id, delegate_->GetURLLoaderFactory(), - &access_token_consumer)); + &access_token_consumer); task_environment_.RunUntilIdle(); fetcher->Start("client_id", "client_secret", scopes); task_environment_.RunUntilIdle(); @@ -556,9 +584,9 @@ TEST_F(CrOSOAuthDelegateTest, BackOffIsTriggerredForTransientErrors) { // Pretend that backoff has expired and try again. delegate_->backoff_entry_.SetCustomReleaseTime(base::TimeTicks()); - fetcher.reset(delegate_->CreateAccessTokenFetcher( + fetcher = delegate_->CreateAccessTokenFetcher( account_info_.account_id, delegate_->GetURLLoaderFactory(), - &access_token_consumer)); + &access_token_consumer); fetcher->Start("client_id", "client_secret", scopes); task_environment_.RunUntilIdle(); EXPECT_EQ(1, access_token_consumer.num_access_token_fetch_success_); @@ -579,10 +607,10 @@ TEST_F(CrOSOAuthDelegateTest, BackOffIsResetOnNetworkChange) { EXPECT_EQ(0, access_token_consumer.num_access_token_fetch_success_); EXPECT_EQ(0, access_token_consumer.num_access_token_fetch_failure_); std::vector<std::string> scopes{"scope"}; - std::unique_ptr<OAuth2AccessTokenFetcher> fetcher( + std::unique_ptr<OAuth2AccessTokenFetcher> fetcher = delegate_->CreateAccessTokenFetcher(account_info_.account_id, delegate_->GetURLLoaderFactory(), - &access_token_consumer)); + &access_token_consumer); task_environment_.RunUntilIdle(); fetcher->Start("client_id", "client_secret", scopes); task_environment_.RunUntilIdle(); @@ -594,9 +622,9 @@ TEST_F(CrOSOAuthDelegateTest, BackOffIsResetOnNetworkChange) { // Notify of network change and ensure that request now runs. delegate_->OnConnectionChanged( network::mojom::ConnectionType::CONNECTION_WIFI); - fetcher.reset(delegate_->CreateAccessTokenFetcher( + fetcher = delegate_->CreateAccessTokenFetcher( account_info_.account_id, delegate_->GetURLLoaderFactory(), - &access_token_consumer)); + &access_token_consumer); fetcher->Start("client_id", "client_secret", scopes); task_environment_.RunUntilIdle(); EXPECT_EQ(1, access_token_consumer.num_access_token_fetch_success_); diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h index 8815bc9f0d3..f82091685a0 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h @@ -1,47 +1,51 @@ // Copyright 2015 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_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_DELEGATE_H_ -#define COMPONENTS_SIGNIN_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_DELEGATE_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_IOS_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_IOS_H_ +#include <map> +#include <memory> #include <string> +#include <vector> #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/threading/thread_checker.h" -#include "google_apis/gaia/oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" class AccountTrackerService; -class ProfileOAuth2TokenServiceIOSProvider; +class DeviceAccountsProvider; class SigninClient; -class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate { +class ProfileOAuth2TokenServiceIOSDelegate + : public ProfileOAuth2TokenServiceDelegate { public: ProfileOAuth2TokenServiceIOSDelegate( SigninClient* client, - std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider> provider, + std::unique_ptr<DeviceAccountsProvider> provider, AccountTrackerService* account_tracker_service); ~ProfileOAuth2TokenServiceIOSDelegate() override; - OAuth2AccessTokenFetcher* CreateAccessTokenFetcher( - const std::string& account_id, + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) override; // KeyedService void Shutdown() override; - bool RefreshTokenIsAvailable(const std::string& account_id) const override; + bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const override; GoogleServiceAuthError GetAuthError( - const std::string& account_id) const override; - void UpdateAuthError(const std::string& account_id, + const CoreAccountId& account_id) const override; + void UpdateAuthError(const CoreAccountId& account_id, const GoogleServiceAuthError& error) override; - void LoadCredentials(const std::string& primary_account_id) override; - std::vector<std::string> GetAccounts() override; + void LoadCredentials(const CoreAccountId& primary_account_id) override; + std::vector<CoreAccountId> GetAccounts() const override; // This method should not be called when using shared authentication. - void UpdateCredentials(const std::string& account_id, + void UpdateCredentials(const CoreAccountId& account_id, const std::string& refresh_token) override; // Removes all credentials from this instance of |ProfileOAuth2TokenService|, @@ -49,35 +53,32 @@ class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate { // Subsequent calls to |RefreshTokenIsAvailable| will return |false|. void RevokeAllCredentials() override; - void AddAccountFromSystem(const std::string& account_id) override; + void ReloadAllAccountsFromSystem() override; - void ReloadAccountsFromSystem(const std::string& primary_account_id) override; - - // Reloads accounts from the provider. Fires |OnRefreshTokenAvailable| for - // each new account. Fires |OnRefreshTokenRevoked| for each account that was - // removed. - // It expects that there is already a primary account id. - void ReloadCredentials(); - - // Sets the primary account and then reloads the accounts from the provider. - // Should be called when the user signs in to a new account. - // |primary_account_id| must not be an empty string. - void ReloadCredentials(const std::string& primary_account_id); + void ReloadAccountFromSystem(const CoreAccountId& account_id) override; // Adds |account_id| to |accounts_| if it does not exist or udpates // the auth error state of |account_id| if it exists. Fires // |OnRefreshTokenAvailable| if the account info is updated. - virtual void AddOrUpdateAccount(const std::string& account_id); + virtual void AddOrUpdateAccount(const CoreAccountId& account_id); protected: // Removes |account_id| from |accounts_|. Fires |OnRefreshTokenRevoked| // if the account info is removed. - virtual void RemoveAccount(const std::string& account_id); + virtual void RemoveAccount(const CoreAccountId& account_id); private: friend class ProfileOAuth2TokenServiceIOSDelegateTest; FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest, LoadRevokeCredentialsClearsExcludedAccounts); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest, + ReloadCredentials); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest, + ReloadCredentialsWithPrimaryAccountId); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest, + UpdateAuthErrorAfterRevokeCredentials); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest, + GetAuthError); struct AccountStatus { GoogleServiceAuthError last_auth_error; @@ -85,27 +86,29 @@ class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate { // Maps the |account_id| of accounts known to ProfileOAuth2TokenService // to information about the account. - typedef std::map<std::string, AccountStatus> AccountStatusMap; + typedef std::map<CoreAccountId, AccountStatus> AccountStatusMap; + + // Reloads accounts from the provider. Fires |OnRefreshTokenAvailable| for + // each new account. Fires |OnRefreshTokenRevoked| for each account that was + // removed. + void ReloadCredentials(); // Clears exclude secondary accounts preferences. void ClearExcludedSecondaryAccounts(); - // The primary account id. - std::string primary_account_id_; - // Info about the existing accounts. AccountStatusMap accounts_; // Calls to this class are expected to be made from the browser UI thread. // The purpose of this checker is to detect access to // ProfileOAuth2TokenService from multiple threads in upstream code. - base::ThreadChecker thread_checker_; + THREAD_CHECKER(thread_checker_); - // The client with which this instance was initialied, or NULL. - SigninClient* client_; - std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider> provider_; + // The client with which this instance was initialied, or null. + SigninClient* client_ = nullptr; + std::unique_ptr<DeviceAccountsProvider> provider_; AccountTrackerService* account_tracker_service_; DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceIOSDelegate); }; -#endif // COMPONENTS_SIGNIN_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_DELEGATE_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PROFILE_OAUTH2_TOKEN_SERVICE_DELEGATE_IOS_H_ diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm index d931066f992..17e96fd1c93 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h" #import <Foundation/Foundation.h> -#include <memory> #include <set> -#include <string> -#include <vector> +#include <utility> #include "base/bind.h" #include "base/macros.h" @@ -18,11 +16,11 @@ #include "base/values.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/signin_client.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/base/signin_pref_names.h" +#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" @@ -35,7 +33,7 @@ namespace { // Match the way Chromium handles authentication errors in // google_apis/gaia/oauth2_access_token_fetcher.cc: GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError( - ProfileOAuth2TokenServiceIOSProvider* provider, + DeviceAccountsProvider* provider, const std::string& gaia_id, NSError* error) { if (!error) @@ -70,10 +68,20 @@ GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError( } } +// Converts a DeviceAccountsProvider::AccountInfo to an AccountInfo. +AccountInfo AccountInfoFromDeviceAccount( + const DeviceAccountsProvider::AccountInfo& account) { + AccountInfo account_info; + account_info.email = account.email; + account_info.gaia = account.gaia; + account_info.hosted_domain = account.hosted_domain; + return account_info; +} + class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher { public: SSOAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer, - ProfileOAuth2TokenServiceIOSProvider* provider, + DeviceAccountsProvider* provider, const AccountInfo& account); ~SSOAccessTokenFetcher() override; @@ -89,7 +97,7 @@ class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher { NSError* error); private: - ProfileOAuth2TokenServiceIOSProvider* provider_; // weak + DeviceAccountsProvider* provider_; // weak AccountInfo account_; bool request_was_cancelled_; base::WeakPtrFactory<SSOAccessTokenFetcher> weak_factory_; @@ -99,7 +107,7 @@ class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher { SSOAccessTokenFetcher::SSOAccessTokenFetcher( OAuth2AccessTokenConsumer* consumer, - ProfileOAuth2TokenServiceIOSProvider* provider, + DeviceAccountsProvider* provider, const AccountInfo& account) : OAuth2AccessTokenFetcher(consumer), provider_(provider), @@ -109,8 +117,7 @@ SSOAccessTokenFetcher::SSOAccessTokenFetcher( DCHECK(provider_); } -SSOAccessTokenFetcher::~SSOAccessTokenFetcher() { -} +SSOAccessTokenFetcher::~SSOAccessTokenFetcher() {} void SSOAccessTokenFetcher::Start(const std::string& client_id, const std::string& client_secret_unused, @@ -118,8 +125,8 @@ void SSOAccessTokenFetcher::Start(const std::string& client_id, std::set<std::string> scopes_set(scopes.begin(), scopes.end()); provider_->GetAccessToken( account_.gaia, client_id, scopes_set, - base::Bind(&SSOAccessTokenFetcher::OnAccessTokenResponse, - weak_factory_.GetWeakPtr())); + base::BindOnce(&SSOAccessTokenFetcher::OnAccessTokenResponse, + weak_factory_.GetWeakPtr())); } void SSOAccessTokenFetcher::CancelRequest() { @@ -149,7 +156,7 @@ void SSOAccessTokenFetcher::OnAccessTokenResponse(NSString* token, ProfileOAuth2TokenServiceIOSDelegate::ProfileOAuth2TokenServiceIOSDelegate( SigninClient* client, - std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider> provider, + std::unique_ptr<DeviceAccountsProvider> provider, AccountTrackerService* account_tracker_service) : client_(client), provider_(std::move(provider)), @@ -160,20 +167,22 @@ ProfileOAuth2TokenServiceIOSDelegate::ProfileOAuth2TokenServiceIOSDelegate( } ProfileOAuth2TokenServiceIOSDelegate::~ProfileOAuth2TokenServiceIOSDelegate() { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } void ProfileOAuth2TokenServiceIOSDelegate::Shutdown() { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); accounts_.clear(); } void ProfileOAuth2TokenServiceIOSDelegate::LoadCredentials( - const std::string& primary_account_id) { - DCHECK(thread_checker_.CalledOnValidThread()); + const CoreAccountId& primary_account_id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK_EQ(LOAD_CREDENTIALS_NOT_STARTED, load_credentials_state()); - set_load_credentials_state(LOAD_CREDENTIALS_IN_PROGRESS); + DCHECK_EQ(signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED, + load_credentials_state()); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS); // Clean-up stale data from prefs. ClearExcludedSecondaryAccounts(); @@ -181,32 +190,21 @@ void ProfileOAuth2TokenServiceIOSDelegate::LoadCredentials( if (primary_account_id.empty()) { // On startup, always fire refresh token loaded even if there is nothing // to load (not authenticated). - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); FireRefreshTokensLoaded(); return; } - ReloadCredentials(primary_account_id); + ReloadCredentials(); - set_load_credentials_state(LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); + set_load_credentials_state( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS); FireRefreshTokensLoaded(); } -void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials( - const std::string& primary_account_id) { - DCHECK(!primary_account_id.empty()); - DCHECK(primary_account_id_.empty() || - primary_account_id_ == primary_account_id); - primary_account_id_ = primary_account_id; - ReloadCredentials(); -} - void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials() { - DCHECK(thread_checker_.CalledOnValidThread()); - if (primary_account_id_.empty()) { - // Avoid loading the credentials if there is no primary account id. - return; - } + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Get the list of new account ids. std::set<std::string> new_account_ids; @@ -219,20 +217,20 @@ void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials() { // a fetch access token operation when it receives a // |OnRefreshTokenAvailable| notification. std::string account_id = account_tracker_service_->SeedAccountInfo( - new_account.gaia, new_account.email); + AccountInfoFromDeviceAccount(new_account)); new_account_ids.insert(account_id); } // Get the list of existing account ids. - std::vector<std::string> old_account_ids = GetAccounts(); + std::vector<CoreAccountId> old_account_ids = GetAccounts(); std::sort(old_account_ids.begin(), old_account_ids.end()); - std::set<std::string> accounts_to_add = - base::STLSetDifference<std::set<std::string>>(new_account_ids, - old_account_ids); - std::set<std::string> accounts_to_remove = - base::STLSetDifference<std::set<std::string>>(old_account_ids, - new_account_ids); + std::set<CoreAccountId> accounts_to_add = + base::STLSetDifference<std::set<CoreAccountId>>(new_account_ids, + old_account_ids); + std::set<CoreAccountId> accounts_to_remove = + base::STLSetDifference<std::set<CoreAccountId>>(old_account_ids, + new_account_ids); if (accounts_to_add.empty() && accounts_to_remove.empty()) return; @@ -250,15 +248,15 @@ void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials() { } void ProfileOAuth2TokenServiceIOSDelegate::UpdateCredentials( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& refresh_token) { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); NOTREACHED() << "Unexpected call to UpdateCredentials when using shared " "authentication."; } void ProfileOAuth2TokenServiceIOSDelegate::RevokeAllCredentials() { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ScopedBatchChange batch(this); AccountStatusMap toRemove = accounts_; @@ -266,59 +264,56 @@ void ProfileOAuth2TokenServiceIOSDelegate::RevokeAllCredentials() { RemoveAccount(accountStatus.first); DCHECK_EQ(0u, accounts_.size()); - primary_account_id_.clear(); ClearExcludedSecondaryAccounts(); } -void ProfileOAuth2TokenServiceIOSDelegate::AddAccountFromSystem( - const std::string& account_id) { - AddOrUpdateAccount(account_id); +void ProfileOAuth2TokenServiceIOSDelegate::ReloadAllAccountsFromSystem() { + ReloadCredentials(); } -void ProfileOAuth2TokenServiceIOSDelegate::ReloadAccountsFromSystem( - const std::string& primary_account_id) { - if (primary_account_id.empty()) - return; - - ReloadCredentials(primary_account_id); +void ProfileOAuth2TokenServiceIOSDelegate::ReloadAccountFromSystem( + const CoreAccountId& account_id) { + AddOrUpdateAccount(account_id); } -OAuth2AccessTokenFetcher* +std::unique_ptr<OAuth2AccessTokenFetcher> ProfileOAuth2TokenServiceIOSDelegate::CreateAccessTokenFetcher( - const std::string& account_id, + const CoreAccountId& account_id, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, OAuth2AccessTokenConsumer* consumer) { AccountInfo account_info = account_tracker_service_->GetAccountInfo(account_id); - return new SSOAccessTokenFetcher(consumer, provider_.get(), account_info); + return std::make_unique<SSOAccessTokenFetcher>(consumer, provider_.get(), + account_info); } -std::vector<std::string> ProfileOAuth2TokenServiceIOSDelegate::GetAccounts() { - DCHECK(thread_checker_.CalledOnValidThread()); - std::vector<std::string> account_ids; - for (auto i = accounts_.begin(); i != accounts_.end(); ++i) - account_ids.push_back(i->first); +std::vector<CoreAccountId> ProfileOAuth2TokenServiceIOSDelegate::GetAccounts() + const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + std::vector<CoreAccountId> account_ids; + for (const auto& account : accounts_) + account_ids.push_back(account.first); return account_ids; } bool ProfileOAuth2TokenServiceIOSDelegate::RefreshTokenIsAvailable( - const std::string& account_id) const { - DCHECK(thread_checker_.CalledOnValidThread()); + const CoreAccountId& account_id) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return accounts_.count(account_id) > 0; } GoogleServiceAuthError ProfileOAuth2TokenServiceIOSDelegate::GetAuthError( - const std::string& account_id) const { + const CoreAccountId& account_id) const { auto it = accounts_.find(account_id); return (it == accounts_.end()) ? GoogleServiceAuthError::AuthErrorNone() : it->second.last_auth_error; } void ProfileOAuth2TokenServiceIOSDelegate::UpdateAuthError( - const std::string& account_id, + const CoreAccountId& account_id, const GoogleServiceAuthError& error) { - DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Do not report connection errors as these are not actually auth errors. // We also want to avoid masking a "real" auth error just because we @@ -341,8 +336,8 @@ void ProfileOAuth2TokenServiceIOSDelegate::UpdateAuthError( // Clear the authentication error state and notify all observers that a new // refresh token is available so that they request new access tokens. void ProfileOAuth2TokenServiceIOSDelegate::AddOrUpdateAccount( - const std::string& account_id) { - DCHECK(thread_checker_.CalledOnValidThread()); + const CoreAccountId& account_id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Account must have been seeded before attempting to add it. DCHECK(!account_tracker_service_->GetAccountInfo(account_id).gaia.empty()); @@ -363,8 +358,8 @@ void ProfileOAuth2TokenServiceIOSDelegate::AddOrUpdateAccount( } void ProfileOAuth2TokenServiceIOSDelegate::RemoveAccount( - const std::string& account_id) { - DCHECK(thread_checker_.CalledOnValidThread()); + const CoreAccountId& account_id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!account_id.empty()); if (accounts_.count(account_id) > 0) { diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm index 38767190d58..0ae0cd018da 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm @@ -2,37 +2,39 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/test_signin_client.h" -#include "components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/ios/fake_device_accounts_provider.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" -#include "google_apis/gaia/oauth2_token_service_test_util.h" +#include "google_apis/gaia/oauth2_access_token_fetcher.h" +#include "google_apis/gaia/oauth2_access_token_manager_test_util.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" #include "net/url_request/test_url_fetcher_factory.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -typedef ProfileOAuth2TokenServiceIOSProvider::AccountInfo ProviderAccount; +typedef DeviceAccountsProvider::AccountInfo ProviderAccount; class ProfileOAuth2TokenServiceIOSDelegateTest - : public testing::Test, + : public PlatformTest, public OAuth2AccessTokenConsumer, - public OAuth2TokenService::Observer { + public OAuth2TokenServiceObserver { public: ProfileOAuth2TokenServiceIOSDelegateTest() - : factory_(NULL), + : factory_(nullptr), client_(&prefs_), token_available_count_(0), token_revoked_count_(0), @@ -51,7 +53,7 @@ class ProfileOAuth2TokenServiceIOSDelegateTest prefs_.registry()->RegisterListPref( prefs::kTokenServiceExcludedSecondaryAccounts); - fake_provider_ = new FakeProfileOAuth2TokenServiceIOSProvider(); + fake_provider_ = new FakeDeviceAccountsProvider(); factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_revoke_url(), "", net::HTTP_OK, net::URLRequestStatus::SUCCESS); oauth2_delegate_.reset(new ProfileOAuth2TokenServiceIOSDelegate( @@ -75,15 +77,15 @@ class ProfileOAuth2TokenServiceIOSDelegateTest last_access_token_error_ = error; } - // OAuth2TokenService::Observer implementation. - void OnRefreshTokenAvailable(const std::string& account_id) override { + // OAuth2TokenServiceObserver implementation. + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override { ++token_available_count_; } - void OnRefreshTokenRevoked(const std::string& account_id) override { + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override { ++token_revoked_count_; } void OnRefreshTokensLoaded() override { ++tokens_loaded_count_; } - void OnAuthErrorChanged(const std::string& account_id, + void OnAuthErrorChanged(const CoreAccountId& account_id, const GoogleServiceAuthError& error) override { ++auth_error_changed_count_; } @@ -103,14 +105,14 @@ class ProfileOAuth2TokenServiceIOSDelegateTest } protected: - base::MessageLoop message_loop_; + base::test::ScopedTaskEnvironment scoped_task_environment_; net::FakeURLFetcherFactory factory_; TestingPrefServiceSimple prefs_; TestSigninClient client_; AccountTrackerService account_tracker_; - FakeProfileOAuth2TokenServiceIOSProvider* fake_provider_; + FakeDeviceAccountsProvider* fake_provider_; std::unique_ptr<ProfileOAuth2TokenServiceIOSDelegate> oauth2_delegate_; - TestingOAuth2TokenServiceConsumer consumer_; + TestingOAuth2AccessTokenManagerConsumer consumer_; int token_available_count_; int token_revoked_count_; int tokens_loaded_count_; @@ -201,26 +203,10 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ReloadCredentials) { } TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, - ReloadCredentialsIgnoredIfNoPrimaryAccountId) { - ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x"); - ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x"); - oauth2_delegate_->ReloadCredentials(); - - EXPECT_EQ(0, token_available_count_); - EXPECT_EQ(0, tokens_loaded_count_); - EXPECT_EQ(0, token_revoked_count_); - EXPECT_EQ(0U, oauth2_delegate_->GetAccounts().size()); - EXPECT_FALSE( - oauth2_delegate_->RefreshTokenIsAvailable(GetAccountId(account1))); - EXPECT_FALSE( - oauth2_delegate_->RefreshTokenIsAvailable(GetAccountId(account2))); -} - -TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ReloadCredentialsWithPrimaryAccountId) { ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x"); ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x"); - oauth2_delegate_->ReloadCredentials(GetAccountId(account1)); + oauth2_delegate_->ReloadCredentials(); EXPECT_EQ(2, token_available_count_); EXPECT_EQ(0, tokens_loaded_count_); @@ -285,7 +271,7 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestFailure) { TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, UpdateAuthErrorAfterRevokeCredentials) { ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x"); - oauth2_delegate_->ReloadCredentials(GetAccountId(account1)); + oauth2_delegate_->ReloadCredentials(); base::RunLoop().RunUntilIdle(); ResetObserverCounts(); @@ -303,7 +289,7 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, GetAuthError) { // Accounts have no error by default. ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x"); - oauth2_delegate_->ReloadCredentials(GetAccountId(account1)); + oauth2_delegate_->ReloadCredentials(); base::RunLoop().RunUntilIdle(); EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), oauth2_delegate_->GetAuthError("gaia_1")); diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_unittest.cc new file mode 100644 index 00000000000..f6132ef08f7 --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_unittest.cc @@ -0,0 +1,57 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> + +#include <string> + +#include "base/macros.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +#include "services/network/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +// The default implementation of +// ProfileOAuth2TokenServiceDelegate::InvalidateTokensForMultilogin is used on +// mobile where refresh tokens are not accessible. This test checks that refresh +// tokens are not affected on INVALID_TOKENS Multilogin error. +TEST(ProfileOAuth2TokenServiceDelegateTest, InvalidateTokensForMultilogin) { + class TestOAuth2TokenServiceObserver : public OAuth2TokenServiceObserver { + public: + MOCK_METHOD2(OnAuthErrorChanged, + void(const CoreAccountId&, const GoogleServiceAuthError&)); + }; + + FakeProfileOAuth2TokenServiceDelegate delegate; + + TestOAuth2TokenServiceObserver observer; + delegate.AddObserver(&observer); + // Check that OnAuthErrorChanged is not fired from + // InvalidateTokensForMultilogin and refresh tokens are not set in error. + EXPECT_CALL( + observer, + OnAuthErrorChanged(::testing::_, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS))) + .Times(0); + + const CoreAccountId account_id1("account_id1"); + const CoreAccountId account_id2("account_id2"); + + delegate.UpdateCredentials(account_id1, "refresh_token1"); + delegate.UpdateCredentials(account_id2, "refresh_token2"); + + delegate.InvalidateTokenForMultilogin(account_id1); + + EXPECT_EQ(delegate.GetAuthError(account_id1).state(), + GoogleServiceAuthError::NONE); + EXPECT_EQ(delegate.GetAuthError(account_id2).state(), + GoogleServiceAuthError::NONE); + + delegate.RemoveObserver(&observer); +} diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc new file mode 100644 index 00000000000..c77c9336b7f --- /dev/null +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_unittest.cc @@ -0,0 +1,824 @@ +// Copyright 2013 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/internal/identity_manager/profile_oauth2_token_service.h" + +#include <stddef.h> +#include <string> + +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/threading/platform_thread.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_access_token_consumer.h" +#include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h" +#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" +#include "google_apis/gaia/oauth2_access_token_manager_test_util.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_test_util.h" +#include "services/network/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +// A testing consumer that retries on error. +class RetryingTestingOAuth2AccessTokenManagerConsumer + : public TestingOAuth2AccessTokenManagerConsumer { + public: + RetryingTestingOAuth2AccessTokenManagerConsumer( + ProfileOAuth2TokenService* oauth2_service, + const CoreAccountId& account_id) + : oauth2_service_(oauth2_service), account_id_(account_id) {} + ~RetryingTestingOAuth2AccessTokenManagerConsumer() override {} + + void OnGetTokenFailure(const OAuth2AccessTokenManager::Request* request, + const GoogleServiceAuthError& error) override { + if (retry_counter_ <= 0) + return; + retry_counter_--; + TestingOAuth2AccessTokenManagerConsumer::OnGetTokenFailure(request, error); + request_ = oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), this); + } + + int retry_counter_ = 2; + ProfileOAuth2TokenService* oauth2_service_; + CoreAccountId account_id_; + std::unique_ptr<OAuth2AccessTokenManager::Request> request_; +}; + +class FakeOAuth2TokenServiceObserver : public OAuth2TokenServiceObserver { + public: + MOCK_METHOD2(OnAuthErrorChanged, + void(const CoreAccountId&, const GoogleServiceAuthError&)); +}; + +// This class fakes the behaviour of a MutableProfileOAuth2TokenServiceDelegate +// used on Desktop. +class FakeProfileOAuth2TokenServiceDelegateDesktop + : public FakeProfileOAuth2TokenServiceDelegate { + std::string GetTokenForMultilogin( + const CoreAccountId& account_id) const override { + if (GetAuthError(account_id) == GoogleServiceAuthError::AuthErrorNone()) + return GetRefreshToken(account_id); + return std::string(); + } + + std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher( + const CoreAccountId& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + OAuth2AccessTokenConsumer* consumer) override { + if (GetAuthError(account_id).IsPersistentError()) { + return std::make_unique<OAuth2AccessTokenFetcherImmediateError>( + consumer, GetAuthError(account_id)); + } + return FakeProfileOAuth2TokenServiceDelegate::CreateAccessTokenFetcher( + account_id, url_loader_factory, consumer); + } + void InvalidateTokenForMultilogin( + const CoreAccountId& failed_account) override { + UpdateAuthError(failed_account, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + } +}; + +class ProfileOAuth2TokenServiceTest : public testing::Test { + public: + void SetUp() override { + auto delegate = std::make_unique<FakeProfileOAuth2TokenServiceDelegate>(); + // Save raw delegate pointer for later. + delegate_ptr_ = delegate.get(); + + test_url_loader_factory_ = delegate->test_url_loader_factory(); + oauth2_service_ = std::make_unique<ProfileOAuth2TokenService>( + &prefs_, std::move(delegate)); + account_id_ = CoreAccountId("test_user@gmail.com"); + } + + void TearDown() override { + // Makes sure that all the clean up tasks are run. + base::RunLoop().RunUntilIdle(); + } + + void SimulateOAuthTokenResponse(const std::string& token, + net::HttpStatusCode status = net::HTTP_OK) { + test_url_loader_factory_->AddResponse( + GaiaUrls::GetInstance()->oauth2_token_url().spec(), token, status); + } + + protected: + base::MessageLoopForIO message_loop_; // net:: stuff needs IO message loop. + network::TestURLLoaderFactory* test_url_loader_factory_ = nullptr; + FakeProfileOAuth2TokenServiceDelegate* delegate_ptr_ = nullptr; // Not owned. + std::unique_ptr<ProfileOAuth2TokenService> oauth2_service_; + CoreAccountId account_id_; + TestingOAuth2AccessTokenManagerConsumer consumer_; + TestingPrefServiceSimple prefs_; +}; + +TEST_F(ProfileOAuth2TokenServiceTest, NoOAuth2RefreshToken) { + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, GetAccounts) { + // Accounts should start off empty. + auto accounts = oauth2_service_->GetAccounts(); + EXPECT_TRUE(accounts.empty()); + + // Add an account. + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + // Accounts should still be empty as tokens have not yet been loaded from + // disk. + accounts = oauth2_service_->GetAccounts(); + EXPECT_TRUE(accounts.empty()); + + // Load tokens from disk. + oauth2_service_->GetDelegate()->LoadCredentials(CoreAccountId()); + + // |account_id_| should now be visible in the accounts. + accounts = oauth2_service_->GetAccounts(); + EXPECT_THAT(accounts, testing::ElementsAre(account_id_)); +} + +TEST_F(ProfileOAuth2TokenServiceTest, FailureShouldNotRetry) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + SimulateOAuthTokenResponse("", net::HTTP_UNAUTHORIZED); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); + EXPECT_EQ(0, test_url_loader_factory_->NumPending()); +} + +TEST_F(ProfileOAuth2TokenServiceTest, SuccessWithoutCaching) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, SuccessWithCaching) { + OAuth2AccessTokenManager::ScopeSet scopes1; + scopes1.insert("s1"); + scopes1.insert("s2"); + OAuth2AccessTokenManager::ScopeSet scopes1_same; + scopes1_same.insert("s2"); + scopes1_same.insert("s1"); + OAuth2AccessTokenManager::ScopeSet scopes2; + scopes2.insert("s3"); + + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + // First request. + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest(account_id_, scopes1, &consumer_)); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Second request to the same set of scopes, should return the same token + // without needing a network request. + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest(account_id_, scopes1_same, &consumer_)); + base::RunLoop().RunUntilIdle(); + + // No new network fetcher. + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Third request to a new set of scopes, should return another token. + SimulateOAuthTokenResponse(GetValidTokenResponse("token2", 3600)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request3( + oauth2_service_->StartRequest(account_id_, scopes2, &consumer_)); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(3, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token2", consumer_.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, SuccessAndExpirationAndFailure) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + // First request. + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 0)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Second request must try to access the network as the token has expired. + SimulateOAuthTokenResponse("", net::HTTP_UNAUTHORIZED); // Network failure. + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + // First request. + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 0)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Second request must try to access the network as the token has expired. + SimulateOAuthTokenResponse(GetValidTokenResponse("another token", 0)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("another token", consumer_.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, RequestDeletedBeforeCompletion) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ(1, test_url_loader_factory_->NumPending()); + + request.reset(); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, RequestDeletedAfterCompletion) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + request.reset(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, + MultipleRequestsForTheSameScopesWithOneDeleted) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + + request.reset(); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, + ClearedRefreshTokenFailsSubsequentRequests) { + // We have a valid refresh token; the first request is successful. + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // The refresh token is no longer available; subsequent requests fail. + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, ""); + request = oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, + ChangedRefreshTokenCancelsInFlightRequests) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "first refreshToken"); + OAuth2AccessTokenManager::ScopeSet scopes; + scopes.insert("s1"); + scopes.insert("s2"); + OAuth2AccessTokenManager::ScopeSet scopes1; + scopes.insert("s3"); + scopes.insert("s4"); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest(account_id_, scopes, &consumer_)); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(1, test_url_loader_factory_->NumPending()); + + // Note |request| is still pending when the refresh token changes. + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "second refreshToken"); + + // UpdateCredentials() triggers OnRefreshTokenAvailable() which causes + // CancelRequestsForAccount(). |consumer_| should have an error and the + // request pending should be 0 since it's canceled at + // OnRefreshTokenAvailable(). + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); + ASSERT_EQ(0, test_url_loader_factory_->NumPending()); + + // Verify that an access token request started after the refresh token was + // updated can complete successfully. + TestingOAuth2AccessTokenManagerConsumer consumer2; + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest(account_id_, scopes1, &consumer2)); + base::RunLoop().RunUntilIdle(); + + network::URLLoaderCompletionStatus ok_status(net::OK); + network::ResourceResponseHead response_head = + network::CreateResourceResponseHead(net::HTTP_OK); + EXPECT_TRUE(test_url_loader_factory_->SimulateResponseForPendingRequest( + GaiaUrls::GetInstance()->oauth2_token_url(), ok_status, response_head, + GetValidTokenResponse("second token", 3600), + network::TestURLLoaderFactory::kMostRecentMatch)); + EXPECT_EQ(1, consumer2.number_of_successful_tokens_); + EXPECT_EQ(0, consumer2.number_of_errors_); + EXPECT_EQ("second token", consumer2.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, StartRequestForMultiloginDesktop) { + class MockOAuth2AccessTokenConsumer + : public TestingOAuth2AccessTokenManagerConsumer { + public: + MockOAuth2AccessTokenConsumer() = default; + ~MockOAuth2AccessTokenConsumer() = default; + + MOCK_METHOD2( + OnGetTokenSuccess, + void(const OAuth2AccessTokenManager::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response)); + + MOCK_METHOD2(OnGetTokenFailure, + void(const OAuth2AccessTokenManager::Request* request, + const GoogleServiceAuthError& error)); + + DISALLOW_COPY_AND_ASSIGN(MockOAuth2AccessTokenConsumer); + }; + ProfileOAuth2TokenService token_service( + &prefs_, + std::make_unique<FakeProfileOAuth2TokenServiceDelegateDesktop>()); + + token_service.GetDelegate()->UpdateCredentials(account_id_, "refreshToken"); + const CoreAccountId account_id_2("account_id_2"); + token_service.GetDelegate()->UpdateCredentials(account_id_2, "refreshToken"); + token_service.GetDelegate()->UpdateAuthError( + account_id_2, + GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + + MockOAuth2AccessTokenConsumer consumer; + + EXPECT_CALL(consumer, OnGetTokenSuccess(::testing::_, ::testing::_)).Times(1); + EXPECT_CALL( + consumer, + OnGetTokenFailure( + ::testing::_, + GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP))) + .Times(1); + EXPECT_CALL( + consumer, + OnGetTokenFailure(::testing::_, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS))) + .Times(1); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request1( + token_service.StartRequestForMultilogin(account_id_, &consumer)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + token_service.StartRequestForMultilogin(account_id_2, &consumer)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request3( + token_service.StartRequestForMultilogin(CoreAccountId("unknown_account"), + &consumer)); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(ProfileOAuth2TokenServiceTest, StartRequestForMultiloginMobile) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequestForMultilogin(account_id_, &consumer_)); + + base::RunLoop().RunUntilIdle(); + network::URLLoaderCompletionStatus ok_status(net::OK); + network::ResourceResponseHead response_head = + network::CreateResourceResponseHead(net::HTTP_OK); + EXPECT_TRUE(test_url_loader_factory_->SimulateResponseForPendingRequest( + GaiaUrls::GetInstance()->oauth2_token_url(), ok_status, response_head, + GetValidTokenResponse("second token", 3600), + network::TestURLLoaderFactory::kMostRecentMatch)); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, ServiceShutDownBeforeFetchComplete) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + // The destructor should cancel all in-flight fetchers. + oauth2_service_.reset(nullptr); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(1, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, RetryingConsumer) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + RetryingTestingOAuth2AccessTokenManagerConsumer consumer( + oauth2_service_.get(), account_id_); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer)); + EXPECT_EQ(0, consumer.number_of_successful_tokens_); + EXPECT_EQ(0, consumer.number_of_errors_); + + SimulateOAuthTokenResponse("", net::HTTP_UNAUTHORIZED); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer.number_of_successful_tokens_); + EXPECT_EQ(2, consumer.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, InvalidateTokensForMultiloginDesktop) { + auto delegate = + std::make_unique<FakeProfileOAuth2TokenServiceDelegateDesktop>(); + ProfileOAuth2TokenService token_service(&prefs_, std::move(delegate)); + FakeOAuth2TokenServiceObserver observer; + token_service.GetDelegate()->AddObserver(&observer); + EXPECT_CALL( + observer, + OnAuthErrorChanged(account_id_, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS))) + .Times(1); + + token_service.GetDelegate()->UpdateCredentials(account_id_, "refreshToken"); + const CoreAccountId account_id_2("account_id_2"); + token_service.GetDelegate()->UpdateCredentials(account_id_2, "refreshToken2"); + token_service.InvalidateTokenForMultilogin(account_id_, "refreshToken"); + // Check that refresh tokens for failed accounts are set in error. + EXPECT_EQ(token_service.GetDelegate()->GetAuthError(account_id_).state(), + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); + EXPECT_EQ(token_service.GetDelegate()->GetAuthError(account_id_2).state(), + GoogleServiceAuthError::NONE); + + token_service.GetDelegate()->RemoveObserver(&observer); +} + +TEST_F(ProfileOAuth2TokenServiceTest, InvalidateTokensForMultiloginMobile) { + FakeOAuth2TokenServiceObserver observer; + oauth2_service_->GetDelegate()->AddObserver(&observer); + EXPECT_CALL( + observer, + OnAuthErrorChanged(account_id_, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS))) + .Times(0); + + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + const CoreAccountId account_id_2("account_id_2"); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_2, + "refreshToken2"); + ; + oauth2_service_->InvalidateTokenForMultilogin(account_id_, "refreshToken"); + // Check that refresh tokens are not affected. + EXPECT_EQ(oauth2_service_->GetDelegate()->GetAuthError(account_id_).state(), + GoogleServiceAuthError::NONE); + EXPECT_EQ(oauth2_service_->GetDelegate()->GetAuthError(account_id_2).state(), + GoogleServiceAuthError::NONE); + + oauth2_service_->GetDelegate()->RemoveObserver(&observer); +} + +TEST_F(ProfileOAuth2TokenServiceTest, InvalidateToken) { + OAuth2AccessTokenManager::ScopeSet scopes; + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + + // First request. + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest(account_id_, scopes, &consumer_)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Second request, should return the same token without needing a network + // request. + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest(account_id_, scopes, &consumer_)); + base::RunLoop().RunUntilIdle(); + + // No new network fetcher. + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + + // Clear previous response so the token request will be pending and we can + // simulate a response after it started. + test_url_loader_factory_->ClearResponses(); + + // Invalidating the token should return a new token on the next request. + oauth2_service_->InvalidateAccessToken(account_id_, scopes, + consumer_.last_token_); + std::unique_ptr<OAuth2AccessTokenManager::Request> request3( + oauth2_service_->StartRequest(account_id_, scopes, &consumer_)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token2", 3600)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(3, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token2", consumer_.last_token_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, CancelAllRequests) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + const CoreAccountId account_id_2("account_id_2"); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_2, + "refreshToken2"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + oauth2_service_->CancelAllRequests(); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(2, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, CancelRequestsForAccount) { + OAuth2AccessTokenManager::ScopeSet scope_set_1; + scope_set_1.insert("scope1"); + scope_set_1.insert("scope2"); + OAuth2AccessTokenManager::ScopeSet scope_set_2(scope_set_1.begin(), + scope_set_1.end()); + scope_set_2.insert("scope3"); + + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request1( + oauth2_service_->StartRequest(account_id_, scope_set_1, &consumer_)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequest(account_id_, scope_set_2, &consumer_)); + + const CoreAccountId account_id_2("account_id_2"); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_2, + "refreshToken2"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request3( + oauth2_service_->StartRequest(account_id_2, scope_set_1, &consumer_)); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + oauth2_service_->CancelRequestsForAccount(account_id_); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(2, consumer_.number_of_errors_); + + oauth2_service_->CancelRequestsForAccount(account_id_2); + + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(3, consumer_.number_of_errors_); +} + +TEST_F(ProfileOAuth2TokenServiceTest, SameScopesRequestedForDifferentClients) { + std::string client_id_1("client1"); + std::string client_secret_1("secret1"); + std::string client_id_2("client2"); + std::string client_secret_2("secret2"); + std::set<std::string> scope_set; + scope_set.insert("scope1"); + scope_set.insert("scope2"); + + std::string refresh_token("refreshToken"); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, refresh_token); + + std::unique_ptr<OAuth2AccessTokenManager::Request> request1( + oauth2_service_->StartRequestForClient( + account_id_, client_id_1, client_secret_1, scope_set, &consumer_)); + std::unique_ptr<OAuth2AccessTokenManager::Request> request2( + oauth2_service_->StartRequestForClient( + account_id_, client_id_2, client_secret_2, scope_set, &consumer_)); + // Start a request that should be duplicate of |request1|. + std::unique_ptr<OAuth2AccessTokenManager::Request> request3( + oauth2_service_->StartRequestForClient( + account_id_, client_id_1, client_secret_1, scope_set, &consumer_)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(2U, oauth2_service_->GetNumPendingRequestsForTesting( + client_id_1, account_id_, scope_set)); + ASSERT_EQ(1U, oauth2_service_->GetNumPendingRequestsForTesting( + client_id_2, account_id_, scope_set)); +} + +TEST_F(ProfileOAuth2TokenServiceTest, RequestParametersOrderTest) { + OAuth2AccessTokenManager::ScopeSet set_0; + OAuth2AccessTokenManager::ScopeSet set_1; + set_1.insert("1"); + + const CoreAccountId account_id0("0"); + const CoreAccountId account_id1("1"); + OAuth2AccessTokenManager::RequestParameters params[] = { + OAuth2AccessTokenManager::RequestParameters("0", "0", set_0), + OAuth2AccessTokenManager::RequestParameters("0", "0", set_1), + OAuth2AccessTokenManager::RequestParameters("0", "1", set_0), + OAuth2AccessTokenManager::RequestParameters("0", "1", set_1), + OAuth2AccessTokenManager::RequestParameters("1", "0", set_0), + OAuth2AccessTokenManager::RequestParameters("1", "0", set_1), + OAuth2AccessTokenManager::RequestParameters("1", "1", set_0), + OAuth2AccessTokenManager::RequestParameters("1", "1", set_1), + }; + + for (size_t i = 0; i < base::size(params); i++) { + for (size_t j = 0; j < base::size(params); j++) { + if (i == j) { + EXPECT_FALSE(params[i] < params[j]) << " i=" << i << ", j=" << j; + EXPECT_FALSE(params[j] < params[i]) << " i=" << i << ", j=" << j; + } else if (i < j) { + EXPECT_TRUE(params[i] < params[j]) << " i=" << i << ", j=" << j; + EXPECT_FALSE(params[j] < params[i]) << " i=" << i << ", j=" << j; + } else { + EXPECT_TRUE(params[j] < params[i]) << " i=" << i << ", j=" << j; + EXPECT_FALSE(params[i] < params[j]) << " i=" << i << ", j=" << j; + } + } + } +} + +TEST_F(ProfileOAuth2TokenServiceTest, UpdateClearsCache) { + const CoreAccountId account_id("test@gmail.com"); + std::set<std::string> scope_list; + scope_list.insert("scope"); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id, "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest(account_id, scope_list, &consumer_)); + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_->GetTokenCacheCountForTesting()); + + oauth2_service_->ClearCache(); + + EXPECT_EQ(0, oauth2_service_->GetTokenCacheCountForTesting()); + oauth2_service_->GetDelegate()->UpdateCredentials(account_id, "refreshToken"); + SimulateOAuthTokenResponse(GetValidTokenResponse("another token", 3600)); + request = oauth2_service_->StartRequest(account_id, scope_list, &consumer_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("another token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_->GetTokenCacheCountForTesting()); +} + +TEST_F(ProfileOAuth2TokenServiceTest, FixRequestErrorIfPossible) { + oauth2_service_->GetDelegate()->UpdateCredentials(account_id_, + "refreshToken"); + std::unique_ptr<OAuth2AccessTokenManager::Request> request( + oauth2_service_->StartRequest( + account_id_, OAuth2AccessTokenManager::ScopeSet(), &consumer_)); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + delegate_ptr_->set_fix_request_if_possible(true); + SimulateOAuthTokenResponse("", net::HTTP_UNAUTHORIZED); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + + SimulateOAuthTokenResponse(GetValidTokenResponse("token", 3600)); + base::RunLoop().RunUntilIdle(); + for (int max_reties = 5; + max_reties >= 0 && consumer_.number_of_successful_tokens_ != 1; + --max_reties) { + base::RunLoop().RunUntilIdle(); + base::PlatformThread::Sleep(TimeDelta::FromSeconds(1)); + } + + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); +} diff --git a/chromium/components/signin/core/browser/ubertoken_fetcher_impl.cc b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl.cc index 61501805576..bc5b9f19b3e 100644 --- a/chromium/components/signin/core/browser/ubertoken_fetcher_impl.cc +++ b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl.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/core/browser/ubertoken_fetcher_impl.h" +#include "components/signin/internal/identity_manager/ubertoken_fetcher_impl.h" #include <vector> @@ -13,7 +13,6 @@ #include "base/time/time.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "google_apis/gaia/oauth2_token_service.h" #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" namespace { @@ -31,8 +30,8 @@ namespace signin { const int UbertokenFetcherImpl::kMaxRetries = 3; UbertokenFetcherImpl::UbertokenFetcherImpl( - const std::string& account_id, - OAuth2TokenService* token_service, + const CoreAccountId& account_id, + ProfileOAuth2TokenService* token_service, CompletionCallback ubertoken_callback, gaia::GaiaSource source, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, @@ -47,13 +46,13 @@ UbertokenFetcherImpl::UbertokenFetcherImpl( is_bound_to_channel_id) {} UbertokenFetcherImpl::UbertokenFetcherImpl( - const std::string& account_id, + const CoreAccountId& account_id, const std::string& access_token, - OAuth2TokenService* token_service, + ProfileOAuth2TokenService* token_service, CompletionCallback ubertoken_callback, GaiaAuthFetcherFactory factory, bool is_bound_to_channel_id) - : OAuth2TokenService::Consumer("uber_token_fetcher"), + : OAuth2AccessTokenManager::Consumer("uber_token_fetcher"), token_service_(token_service), ubertoken_callback_(std::move(ubertoken_callback)), is_bound_to_channel_id_(is_bound_to_channel_id), @@ -101,7 +100,7 @@ void UbertokenFetcherImpl::OnUberAuthTokenFailure( } } else { // The access token is invalid. Tell the token service. - OAuth2TokenService::ScopeSet scopes; + OAuth2AccessTokenManager::ScopeSet scopes; scopes.insert(GaiaConstants::kOAuth1LoginScope); token_service_->InvalidateAccessToken(account_id_, scopes, access_token_); @@ -119,7 +118,7 @@ void UbertokenFetcherImpl::OnUberAuthTokenFailure( } void UbertokenFetcherImpl::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const OAuth2AccessTokenConsumer::TokenResponse& token_response) { DCHECK(!token_response.access_token.empty()); access_token_ = token_response.access_token; @@ -128,7 +127,7 @@ void UbertokenFetcherImpl::OnGetTokenSuccess( } void UbertokenFetcherImpl::OnGetTokenFailure( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) { access_token_request_.reset(); std::move(ubertoken_callback_).Run(error, /*access_token=*/std::string()); @@ -139,7 +138,7 @@ void UbertokenFetcherImpl::RequestAccessToken() { gaia_auth_fetcher_.reset(); retry_timer_.Stop(); - OAuth2TokenService::ScopeSet scopes; + OAuth2AccessTokenManager::ScopeSet scopes; scopes.insert(GaiaConstants::kOAuth1LoginScope); access_token_request_ = token_service_->StartRequest(account_id_, scopes, this); diff --git a/chromium/components/signin/core/browser/ubertoken_fetcher_impl.h b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl.h index 423e675fb0d..8f38c5d99a3 100644 --- a/chromium/components/signin/core/browser/ubertoken_fetcher_impl.h +++ b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl.h @@ -2,21 +2,21 @@ // 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_UBERTOKEN_FETCHER_IMPL_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_UBERTOKEN_FETCHER_IMPL_H_ +#ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_UBERTOKEN_FETCHER_IMPL_H_ +#define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_UBERTOKEN_FETCHER_IMPL_H_ #include <memory> #include "base/macros.h" #include "base/timer/timer.h" -#include "components/signin/core/browser/ubertoken_fetcher.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/identity_manager/ubertoken_fetcher.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" -#include "google_apis/gaia/oauth2_token_service.h" // Allow to retrieves an uber-auth token for the user. This class uses the -// |OAuth2TokenService| and considers that the user is already logged in. It -// will use the OAuth2 access token to generate the uber-auth token. +// |ProfileOAuth2TokenService| and considers that the user is already logged in. +// It will use the OAuth2 access token to generate the uber-auth token. // // This class should be used on a single thread, but it can be whichever thread // that you like. @@ -38,7 +38,7 @@ using GaiaAuthFetcherFactory = // Allows to retrieve an uber-auth token. class UbertokenFetcherImpl : public UbertokenFetcher, public GaiaAuthConsumer, - public OAuth2TokenService::Consumer { + public OAuth2AccessTokenManager::Consumer { public: // Maximum number of retries to get the uber-auth token before giving up. static const int kMaxRetries; @@ -51,32 +51,31 @@ class UbertokenFetcherImpl : public UbertokenFetcher, // sequentially for |account_id|. Uses a default GaiaAuthFetcherFactory which // returns base GaiaAuthFetcher instances. UbertokenFetcherImpl( - const std::string& account_id, - OAuth2TokenService* token_service, + const CoreAccountId& account_id, + ProfileOAuth2TokenService* token_service, CompletionCallback ubertoken_callback, gaia::GaiaSource source, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, bool is_bound_to_channel_id = true); // Constructs an instance and starts fetching the ubertoken for |account_id|. - UbertokenFetcherImpl( - const std::string& account_id, - const std::string& access_token, - OAuth2TokenService* token_service, - CompletionCallback ubertoken_callback, - GaiaAuthFetcherFactory factory, - bool is_bound_to_channel_id = true); + UbertokenFetcherImpl(const CoreAccountId& account_id, + const std::string& access_token, + ProfileOAuth2TokenService* token_service, + CompletionCallback ubertoken_callback, + GaiaAuthFetcherFactory factory, + bool is_bound_to_channel_id = true); ~UbertokenFetcherImpl() override; - // Overriden from GaiaAuthConsumer + // Overridden from GaiaAuthConsumer void OnUberAuthTokenSuccess(const std::string& token) override; void OnUberAuthTokenFailure(const GoogleServiceAuthError& error) override; - // Overriden from OAuth2TokenService::Consumer: + // Overridden from OAuth2AccessTokenManager::Consumer: void OnGetTokenSuccess( - const OAuth2TokenService::Request* request, + const OAuth2AccessTokenManager::Request* request, const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, + void OnGetTokenFailure(const OAuth2AccessTokenManager::Request* request, const GoogleServiceAuthError& error) override; private: @@ -86,13 +85,13 @@ class UbertokenFetcherImpl : public UbertokenFetcher, // Exchanges an oauth2 access token for an uber-auth token. void ExchangeTokens(); - OAuth2TokenService* token_service_; + ProfileOAuth2TokenService* token_service_; CompletionCallback ubertoken_callback_; bool is_bound_to_channel_id_; // defaults to true GaiaAuthFetcherFactory gaia_auth_fetcher_factory_; std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; - std::unique_ptr<OAuth2TokenService::Request> access_token_request_; - std::string account_id_; + std::unique_ptr<OAuth2AccessTokenManager::Request> access_token_request_; + CoreAccountId account_id_; std::string access_token_; int retry_number_; base::OneShotTimer retry_timer_; @@ -103,4 +102,4 @@ class UbertokenFetcherImpl : public UbertokenFetcher, } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_UBERTOKEN_FETCHER_IMPL_H_ +#endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_UBERTOKEN_FETCHER_IMPL_H_ diff --git a/chromium/components/signin/core/browser/ubertoken_fetcher_impl_unittest.cc b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl_unittest.cc index 0dbf5eb1662..f4e7d910727 100644 --- a/chromium/components/signin/core/browser/ubertoken_fetcher_impl_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/ubertoken_fetcher_impl_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/core/browser/ubertoken_fetcher_impl.h" +#include "components/signin/internal/identity_manager/ubertoken_fetcher_impl.h" #include <memory> @@ -10,7 +10,8 @@ #include "base/memory/ref_counted.h" #include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" -#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -52,6 +53,7 @@ class UbertokenFetcherImplTest : public testing::Test { UbertokenFetcherImplTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI), + token_service_(&pref_service_), test_shared_loader_factory_( base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( &url_loader_factory_)) { @@ -64,7 +66,8 @@ class UbertokenFetcherImplTest : public testing::Test { protected: base::test::ScopedTaskEnvironment scoped_task_environment_; - FakeOAuth2TokenService token_service_; + TestingPrefServiceSimple pref_service_; + FakeProfileOAuth2TokenService token_service_; network::TestURLLoaderFactory url_loader_factory_; scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; MockUbertokenConsumer consumer_; @@ -74,7 +77,7 @@ class UbertokenFetcherImplTest : public testing::Test { TEST_F(UbertokenFetcherImplTest, Basic) {} TEST_F(UbertokenFetcherImplTest, Success) { - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); fetcher_->OnUberAuthTokenSuccess("uberToken"); @@ -86,7 +89,7 @@ TEST_F(UbertokenFetcherImplTest, Success) { TEST_F(UbertokenFetcherImplTest, NoRefreshToken) { GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP); - fetcher_->OnGetTokenFailure(NULL, error); + fetcher_->OnGetTokenFailure(nullptr, error); EXPECT_EQ(1, consumer_.nb_error_); EXPECT_EQ(0, consumer_.nb_correct_token_); @@ -94,7 +97,7 @@ TEST_F(UbertokenFetcherImplTest, NoRefreshToken) { TEST_F(UbertokenFetcherImplTest, FailureToGetAccessToken) { GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP); - fetcher_->OnGetTokenFailure(NULL, error); + fetcher_->OnGetTokenFailure(nullptr, error); EXPECT_EQ(1, consumer_.nb_error_); EXPECT_EQ(0, consumer_.nb_correct_token_); @@ -103,7 +106,7 @@ TEST_F(UbertokenFetcherImplTest, FailureToGetAccessToken) { TEST_F(UbertokenFetcherImplTest, TransientFailureEventualFailure) { GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); @@ -122,7 +125,7 @@ TEST_F(UbertokenFetcherImplTest, TransientFailureEventualFailure) { TEST_F(UbertokenFetcherImplTest, TransientFailureEventualSuccess) { GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED); - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); @@ -140,7 +143,7 @@ TEST_F(UbertokenFetcherImplTest, TransientFailureEventualSuccess) { } TEST_F(UbertokenFetcherImplTest, PermanentFailureEventualFailure) { - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); @@ -150,7 +153,7 @@ TEST_F(UbertokenFetcherImplTest, PermanentFailureEventualFailure) { EXPECT_EQ(0, consumer_.nb_correct_token_); EXPECT_EQ("", consumer_.last_token_); - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); fetcher_->OnUberAuthTokenFailure(error); @@ -161,7 +164,7 @@ TEST_F(UbertokenFetcherImplTest, PermanentFailureEventualFailure) { TEST_F(UbertokenFetcherImplTest, PermanentFailureEventualSuccess) { GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP); - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); @@ -170,7 +173,7 @@ TEST_F(UbertokenFetcherImplTest, PermanentFailureEventualSuccess) { EXPECT_EQ(0, consumer_.nb_correct_token_); EXPECT_EQ("", consumer_.last_token_); - fetcher_->OnGetTokenSuccess(NULL, + fetcher_->OnGetTokenSuccess(nullptr, OAuth2AccessTokenConsumer::TokenResponse( "accessToken", base::Time(), std::string())); fetcher_->OnUberAuthTokenSuccess("uberToken"); diff --git a/chromium/components/signin/ios/DEPS b/chromium/components/signin/ios/DEPS index bb46705add5..228a84fcbbe 100644 --- a/chromium/components/signin/ios/DEPS +++ b/chromium/components/signin/ios/DEPS @@ -1,5 +1,5 @@ include_rules = [ + "+ios/web/common", "+ios/web/public", - "+services/identity/public/cpp", "+third_party/ocmock", ] diff --git a/chromium/components/signin/ios/browser/BUILD.gn b/chromium/components/signin/ios/browser/BUILD.gn index 07fd4e192ae..7a6fc1ae28e 100644 --- a/chromium/components/signin/ios/browser/BUILD.gn +++ b/chromium/components/signin/ios/browser/BUILD.gn @@ -8,26 +8,27 @@ source_set("browser") { "account_consistency_service.h", "account_consistency_service.mm", "manage_accounts_delegate.h", - "profile_oauth2_token_service_ios_delegate.h", - "profile_oauth2_token_service_ios_delegate.mm", - "profile_oauth2_token_service_ios_provider.h", - "profile_oauth2_token_service_ios_provider.mm", "wait_for_network_callback_helper.cc", "wait_for_network_callback_helper.h", ] deps = [ - ":active_state_manager", "//components/content_settings/core/browser", "//components/google/core/browser", - "//components/keyed_service/core", - "//components/pref_registry", "//components/prefs", "//components/signin/core/browser", - "//google_apis", - "//ios/web", + "//components/signin/public/base", + "//ios/web/common:web_view_creation_util", + "//ios/web/public", + "//url", + ] + + public_deps = [ + ":active_state_manager", + "//base", + "//components/keyed_service/core", + "//components/signin/public/identity_manager", "//net", - "//services/identity/public/cpp:cpp", ] } @@ -45,41 +46,32 @@ source_set("active_state_manager") { ] } -source_set("test_support") { - configs += [ "//build/config/compiler:enable_arc" ] - testonly = true - sources = [ - "fake_profile_oauth2_token_service_ios_provider.h", - "fake_profile_oauth2_token_service_ios_provider.mm", - ] - - public_deps = [ - ":browser", - "//base", - "//google_apis:test_support", - ] -} - source_set("unit_tests") { configs += [ "//build/config/compiler:enable_arc" ] testonly = true sources = [ "account_consistency_service_unittest.mm", "active_state_manager_impl_unittest.mm", - "profile_oauth2_token_service_ios_delegate_unittest.mm", "wait_for_network_callback_helper_unittest.cc", ] deps = [ ":active_state_manager", - ":test_support", - "//components/prefs:test_support", + ":browser", + "//base", + "//components/content_settings/core/browser", + "//components/prefs", "//components/signin/core/browser", + "//components/signin/public/base:test_support", + "//components/signin/public/identity_manager", + "//components/signin/public/identity_manager:test_support", "//components/sync_preferences:test_support", "//ios/web", "//ios/web/public/test", "//ios/web/public/test/fakes", - "//services/identity/public/cpp:test_support", + "//net:test_support", + "//testing/gmock", + "//testing/gtest", "//third_party/ocmock", ] } diff --git a/chromium/components/signin/ios/browser/DEPS b/chromium/components/signin/ios/browser/DEPS new file mode 100644 index 00000000000..1cbae6d899d --- /dev/null +++ b/chromium/components/signin/ios/browser/DEPS @@ -0,0 +1,10 @@ +specific_include_rules = { + "account_consistency_service.mm": [ + "+components/signin/core/browser/account_reconcilor.h", + "+components/signin/core/browser/signin_header_helper.h", + ], + "account_consistency_service_unittest.mm": [ + "+components/signin/core/browser/account_reconcilor.h", + "+components/signin/core/browser/account_reconcilor_delegate.h", + ], +} diff --git a/chromium/components/signin/ios/browser/account_consistency_service.h b/chromium/components/signin/ios/browser/account_consistency_service.h index e70f21b52c8..3d53f0a0f87 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service.h +++ b/chromium/components/signin/ios/browser/account_consistency_service.h @@ -15,11 +15,14 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" -#include "components/content_settings/core/browser/cookie_settings.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/ios/browser/active_state_manager.h" #import "components/signin/ios/browser/manage_accounts_delegate.h" -#import "services/identity/public/cpp/identity_manager.h" +#import "components/signin/public/identity_manager/identity_manager.h" + +namespace content_settings { +class CookieSettings; +} namespace web { class BrowserState; @@ -28,6 +31,7 @@ class WebStatePolicyDecider; } class AccountReconcilor; +class PrefService; @class AccountConsistencyNavigationDelegate; @class WKWebView; @@ -38,7 +42,7 @@ class AccountReconcilor; // // This is currently only used when WKWebView is enabled. class AccountConsistencyService : public KeyedService, - public identity::IdentityManager::Observer, + public signin::IdentityManager::Observer, public ActiveStateManager::Observer { public: // Name of the preference property that persists the domains that have a @@ -50,7 +54,7 @@ class AccountConsistencyService : public KeyedService, PrefService* prefs, AccountReconcilor* account_reconcilor, scoped_refptr<content_settings::CookieSettings> cookie_settings, - identity::IdentityManager* identity_manager); + signin::IdentityManager* identity_manager); ~AccountConsistencyService() override; // Registers the preferences used by AccountConsistencyService. @@ -147,7 +151,7 @@ class AccountConsistencyService : public KeyedService, void OnPrimaryAccountCleared( const CoreAccountInfo& previous_account_info) override; void OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) override; // ActiveStateManager::Observer implementation. @@ -166,7 +170,7 @@ class AccountConsistencyService : public KeyedService, scoped_refptr<content_settings::CookieSettings> cookie_settings_; // Identity manager, observed to be notified of primary account signin and // signout events. - identity::IdentityManager* identity_manager_; + signin::IdentityManager* identity_manager_; // Whether a CHROME_CONNECTED cookie request is currently being applied. bool applying_cookie_requests_; diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm index 66f2a50052a..0f8da0cafce 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service.mm @@ -11,15 +11,17 @@ #import "base/mac/foundation_util.h" #include "base/macros.h" #include "base/strings/sys_string_conversions.h" +#include "components/content_settings/core/browser/cookie_settings.h" #include "components/google/core/common/google_util.h" #include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_reconcilor.h" #include "components/signin/core/browser/signin_header_helper.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "ios/web/common/web_view_creation_util.h" #include "ios/web/public/browser_state.h" -#include "ios/web/public/web_state/web_state_policy_decider.h" -#include "ios/web/public/web_view_creation_util.h" +#import "ios/web/public/navigation/web_state_policy_decider.h" #include "net/base/mac/url_conversions.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "url/gurl.h" @@ -42,7 +44,8 @@ NSString* const kChromeConnectedCookieTemplate = "document.cookie=\"X-CHROME-CONNECTED=; path=/; domain=\" + domain + \";" " expires=Thu, 01-Jan-1970 00:00:01 GMT\";" "document.cookie=\"CHROME_CONNECTED=%@; path=/; domain=\" + domain + \";" - " expires=\" + new Date(%f).toGMTString() + \"; secure;\"</script></html>"; + " expires=\" + new Date(%f).toGMTString() + \"; secure;" + " samesite=lax;\"</script></html>"; // WebStatePolicyDecider that monitors the HTTP headers on Gaia responses, // reacting on the X-Chrome-Manage-Accounts header and notifying its delegate. @@ -219,7 +222,7 @@ AccountConsistencyService::AccountConsistencyService( PrefService* prefs, AccountReconcilor* account_reconcilor, scoped_refptr<content_settings::CookieSettings> cookie_settings, - identity::IdentityManager* identity_manager) + signin::IdentityManager* identity_manager) : browser_state_(browser_state), prefs_(prefs), account_reconcilor_(account_reconcilor), @@ -486,7 +489,7 @@ void AccountConsistencyService::OnPrimaryAccountCleared( } void AccountConsistencyService::OnAccountsInCookieUpdated( - const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info, const GoogleServiceAuthError& error) { AddChromeConnectedCookies(); } diff --git a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm index 0a2e3d0648f..66f0de142e3 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm @@ -11,18 +11,21 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/values.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_reconcilor.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" -#include "components/signin/core/browser/list_accounts_test_utils.h" -#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/public/base/list_accounts_test_utils.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "components/signin/public/identity_manager/test_identity_manager_observer.h" #include "components/sync_preferences/testing_pref_service_syncable.h" +#include "ios/web/public/navigation/web_state_policy_decider.h" #include "ios/web/public/test/fakes/test_browser_state.h" #import "ios/web/public/test/fakes/test_web_state.h" #include "ios/web/public/test/test_web_thread_bundle.h" -#include "ios/web/public/web_state/web_state_policy_decider.h" -#import "services/identity/public/cpp/identity_manager.h" -#import "services/identity/public/cpp/identity_test_environment.h" -#include "services/identity/public/cpp/test_identity_manager_observer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -57,7 +60,7 @@ class FakeAccountConsistencyService : public AccountConsistencyService { PrefService* prefs, AccountReconcilor* account_reconcilor, scoped_refptr<content_settings::CookieSettings> cookie_settings, - identity::IdentityManager* identity_manager) + signin::IdentityManager* identity_manager) : AccountConsistencyService(browser_state, prefs, account_reconcilor, @@ -132,7 +135,7 @@ class AccountConsistencyServiceTest : public PlatformTest { web_view_load_expection_count_ = 0; signin_client_.reset( new TestSigninClient(&prefs_, &test_url_loader_factory_)); - identity_test_env_.reset(new identity::IdentityTestEnvironment( + identity_test_env_.reset(new signin::IdentityTestEnvironment( /*test_url_loader_factory=*/nullptr, &prefs_, signin::AccountConsistencyMethod::kDisabled, signin_client_.get())); settings_map_ = new HostContentSettingsMap( @@ -184,14 +187,14 @@ class AccountConsistencyServiceTest : public PlatformTest { } void SignIn() { - identity::MakePrimaryAccountAvailable( - identity_test_env_->identity_manager(), "user@gmail.com"); + signin::MakePrimaryAccountAvailable(identity_test_env_->identity_manager(), + "user@gmail.com"); EXPECT_EQ(0, web_view_load_expection_count_); } void SignOutAndSimulateGaiaCookieManagerServiceLogout() { - identity::ClearPrimaryAccount(identity_test_env_->identity_manager(), - identity::ClearPrimaryAccountPolicy::DEFAULT); + signin::ClearPrimaryAccount(identity_test_env_->identity_manager(), + signin::ClearPrimaryAccountPolicy::DEFAULT); SimulateGaiaCookieManagerServiceLogout(true); } @@ -238,7 +241,7 @@ class AccountConsistencyServiceTest : public PlatformTest { TestWebState web_state_; network::TestURLLoaderFactory test_url_loader_factory_; - std::unique_ptr<identity::IdentityTestEnvironment> identity_test_env_; + std::unique_ptr<signin::IdentityTestEnvironment> identity_test_env_; // AccountConsistencyService being tested. Actually a // FakeAccountConsistencyService to be able to use a mock web view. std::unique_ptr<AccountConsistencyService> account_consistency_service_; diff --git a/chromium/components/signin/ios/browser/active_state_manager_impl.mm b/chromium/components/signin/ios/browser/active_state_manager_impl.mm index 26a7fc57fa0..494c88e2e20 100644 --- a/chromium/components/signin/ios/browser/active_state_manager_impl.mm +++ b/chromium/components/signin/ios/browser/active_state_manager_impl.mm @@ -7,7 +7,7 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "ios/web/public/browser_state.h" -#include "ios/web/public/web_thread.h" +#include "ios/web/public/thread/web_thread.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." diff --git a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm b/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm deleted file mode 100644 index ceed68ee896..00000000000 --- a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.mm +++ /dev/null @@ -1,83 +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. - -#include "components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h" - -#import <Foundation/Foundation.h> - -#include "base/logging.h" -#include "base/strings/sys_string_conversions.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -FakeProfileOAuth2TokenServiceIOSProvider:: - FakeProfileOAuth2TokenServiceIOSProvider() { -} - -FakeProfileOAuth2TokenServiceIOSProvider:: - ~FakeProfileOAuth2TokenServiceIOSProvider() { -} - -void FakeProfileOAuth2TokenServiceIOSProvider::GetAccessToken( - const std::string& account_id, - const std::string& client_id, - const std::set<std::string>& scopes, - const AccessTokenCallback& callback) { - requests_.push_back(AccessTokenRequest(account_id, callback)); -} - -std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo> -FakeProfileOAuth2TokenServiceIOSProvider::GetAllAccounts() const { - return accounts_; -} - -ProfileOAuth2TokenServiceIOSProvider::AccountInfo -FakeProfileOAuth2TokenServiceIOSProvider::AddAccount(const std::string& gaia, - const std::string& email) { - ProfileOAuth2TokenServiceIOSProvider::AccountInfo account; - account.gaia = gaia; - account.email = email; - accounts_.push_back(account); - return account; -} - -void FakeProfileOAuth2TokenServiceIOSProvider::ClearAccounts() { - accounts_.clear(); -} - -void FakeProfileOAuth2TokenServiceIOSProvider:: - IssueAccessTokenForAllRequests() { - for (auto i = requests_.begin(); i != requests_.end(); ++i) { - std::string account_id = i->first; - AccessTokenCallback callback = i->second; - NSString* access_token = [NSString - stringWithFormat:@"fake_access_token [account=%s]", account_id.c_str()]; - NSDate* one_hour_from_now = [NSDate dateWithTimeIntervalSinceNow:3600]; - callback.Run(access_token, one_hour_from_now, nil); - } - requests_.clear(); -} - -void FakeProfileOAuth2TokenServiceIOSProvider:: - IssueAccessTokenErrorForAllRequests() { - for (auto i = requests_.begin(); i != requests_.end(); ++i) { - std::string account_id = i->first; - AccessTokenCallback callback = i->second; - NSError* error = [[NSError alloc] initWithDomain:@"fake_access_token_error" - code:-1 - userInfo:nil]; - callback.Run(nil, nil, error); - } - requests_.clear(); -} - -AuthenticationErrorCategory -FakeProfileOAuth2TokenServiceIOSProvider::GetAuthenticationErrorCategory( - const std::string& gaia_id, - NSError* error) const { - DCHECK(error); - return kAuthenticationErrorCategoryAuthorizationErrors; -} diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm deleted file mode 100644 index f7b511916d3..00000000000 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.mm +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 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/ios/browser/profile_oauth2_token_service_ios_provider.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo> -ProfileOAuth2TokenServiceIOSProvider::GetAllAccounts() const { - return std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo>(); -} - -void ProfileOAuth2TokenServiceIOSProvider::GetAccessToken( - const std::string& gaia_id, - const std::string& client_id, - const std::set<std::string>& scopes, - const AccessTokenCallback& callback) {} - -AuthenticationErrorCategory -ProfileOAuth2TokenServiceIOSProvider::GetAuthenticationErrorCategory( - const std::string& gaia_id, - NSError* error) const { - return kAuthenticationErrorCategoryUnknownErrors; -} - 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 78fe1caa9b1..7fb8e7cc040 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 @@ -4,6 +4,8 @@ #include "components/signin/ios/browser/wait_for_network_callback_helper.h" +#include <memory> + #include "base/bind.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" @@ -20,12 +22,13 @@ class WaitForNetworkCallbackHelperTest : public testing::Test { int num_callbacks_invoked_; base::test::ScopedTaskEnvironment scoped_task_environment_; - net::test::MockNetworkChangeNotifier network_change_notifier_; + std::unique_ptr<net::test::MockNetworkChangeNotifier> + network_change_notifier_ = net::test::MockNetworkChangeNotifier::Create(); WaitForNetworkCallbackHelper callback_helper_; }; TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedImmediately) { - network_change_notifier_.SetConnectionType( + network_change_notifier_->SetConnectionType( net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); callback_helper_.HandleCallback( base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, @@ -34,7 +37,7 @@ TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedImmediately) { } TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedLater) { - network_change_notifier_.SetConnectionType( + network_change_notifier_->SetConnectionType( net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE); callback_helper_.HandleCallback( base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, @@ -44,9 +47,9 @@ TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedLater) { base::Unretained(this))); EXPECT_EQ(0, num_callbacks_invoked_); - network_change_notifier_.SetConnectionType( + network_change_notifier_->SetConnectionType( net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); - network_change_notifier_.NotifyObserversOfConnectionTypeChangeForTests( + network_change_notifier_->NotifyObserversOfConnectionTypeChangeForTests( net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); scoped_task_environment_.RunUntilIdle(); EXPECT_EQ(2, num_callbacks_invoked_); diff --git a/chromium/components/signin/public/base/BUILD.gn b/chromium/components/signin/public/base/BUILD.gn new file mode 100644 index 00000000000..688252e9342 --- /dev/null +++ b/chromium/components/signin/public/base/BUILD.gn @@ -0,0 +1,93 @@ +# 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. + +import("//build/buildflag_header.gni") +import("//components/signin/features.gni") + +if (is_android) { + import("//build/config/android/rules.gni") +} + +buildflag_header("signin_buildflags") { + header = "signin_buildflags.h" + flags = [ + "ENABLE_DICE_SUPPORT=$enable_dice_support", + "ENABLE_MIRROR=$enable_mirror", + ] +} + +static_library("base") { + sources = [ + "account_consistency_method.cc", + "account_consistency_method.h", + "avatar_icon_util.cc", + "avatar_icon_util.h", + "device_id_helper.cc", + "device_id_helper.h", + "signin_client.cc", + "signin_client.h", + "signin_metrics.cc", + "signin_metrics.h", + "signin_pref_names.cc", + "signin_pref_names.h", + "signin_switches.cc", + "signin_switches.h", + ] + deps = [ + "//components/prefs", + "//third_party/icu:icui18n", + "//third_party/re2", + ] + public_deps = [ + ":signin_buildflags", + "//base", + "//components/keyed_service/core", + "//google_apis", + "//url", + ] + + if (is_chromeos) { + deps += [ "//components/user_manager" ] + } +} + +static_library("test_support") { + testonly = true + sources = [ + "list_accounts_test_utils.cc", + "list_accounts_test_utils.h", + "test_signin_client.cc", + "test_signin_client.h", + ] + + deps = [ + "//base/test:test_support", + "//components/prefs", + "//google_apis:test_support", + ] + + public_deps = [ + ":base", + "//base", + "//services/network:test_support", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "avatar_icon_util_unittest.cc", + "device_id_helper_unittest.cc", + "signin_metrics_unittest.cc", + ] + + deps = [ + ":base", + "//base", + "//base/test:test_support", + "//components/sync_preferences:test_support", + "//testing/gtest", + "//url", + ] +} diff --git a/chromium/components/signin/public/base/DEPS b/chromium/components/signin/public/base/DEPS new file mode 100644 index 00000000000..ab69e125b8b --- /dev/null +++ b/chromium/components/signin/public/base/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/re2/src/re2/re2.h", +] diff --git a/chromium/components/signin/core/browser/account_consistency_method.cc b/chromium/components/signin/public/base/account_consistency_method.cc index b0b031ae515..ba77c9a7afb 100644 --- a/chromium/components/signin/core/browser/account_consistency_method.cc +++ b/chromium/components/signin/public/base/account_consistency_method.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/core/browser/account_consistency_method.h" +#include "components/signin/public/base/account_consistency_method.h" #include "base/logging.h" diff --git a/chromium/components/signin/core/browser/account_consistency_method.h b/chromium/components/signin/public/base/account_consistency_method.h index c46becfe07d..5ca1dfeafa6 100644 --- a/chromium/components/signin/core/browser/account_consistency_method.h +++ b/chromium/components/signin/public/base/account_consistency_method.h @@ -6,8 +6,8 @@ // possible overrides from Experiements. This is done inside chrome/common // because it is accessed by files through the chrome/ directory tree. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_CONSISTENCY_METHOD_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_CONSISTENCY_METHOD_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_ACCOUNT_CONSISTENCY_METHOD_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_ACCOUNT_CONSISTENCY_METHOD_H_ #include "base/feature_list.h" #include "build/build_config.h" @@ -47,4 +47,4 @@ bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a, } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_CONSISTENCY_METHOD_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_ACCOUNT_CONSISTENCY_METHOD_H_ diff --git a/chromium/components/signin/core/browser/avatar_icon_util.cc b/chromium/components/signin/public/base/avatar_icon_util.cc index 2a74566b464..ff2dfa389df 100644 --- a/chromium/components/signin/core/browser/avatar_icon_util.cc +++ b/chromium/components/signin/public/base/avatar_icon_util.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/core/browser/avatar_icon_util.h" +#include "components/signin/public/base/avatar_icon_util.h" #include <string> #include <vector> diff --git a/chromium/components/signin/core/browser/avatar_icon_util.h b/chromium/components/signin/public/base/avatar_icon_util.h index 4209820a8e0..26b6b83c0c4 100644 --- a/chromium/components/signin/core/browser/avatar_icon_util.h +++ b/chromium/components/signin/public/base/avatar_icon_util.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_CORE_BROWSER_AVATAR_ICON_UTIL_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_AVATAR_ICON_UTIL_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_AVATAR_ICON_UTIL_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_AVATAR_ICON_UTIL_H_ class GURL; @@ -24,4 +24,4 @@ GURL GetAvatarImageURLWithOptions(const GURL& old_url, } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_AVATAR_ICON_UTIL_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_AVATAR_ICON_UTIL_H_ diff --git a/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc b/chromium/components/signin/public/base/avatar_icon_util_unittest.cc index f3686240080..0c8f7ce7f45 100644 --- a/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc +++ b/chromium/components/signin/public/base/avatar_icon_util_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/core/browser/avatar_icon_util.h" +#include "components/signin/public/base/avatar_icon_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" diff --git a/chromium/components/signin/core/browser/device_id_helper.cc b/chromium/components/signin/public/base/device_id_helper.cc index 6f0dcbb748b..99c262add34 100644 --- a/chromium/components/signin/core/browser/device_id_helper.cc +++ b/chromium/components/signin/public/base/device_id_helper.cc @@ -2,14 +2,14 @@ // 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/device_id_helper.h" +#include "components/signin/public/base/device_id_helper.h" #include "base/command_line.h" #include "base/guid.h" #include "base/logging.h" #include "components/prefs/pref_service.h" -#include "components/signin/core/browser/signin_pref_names.h" -#include "components/signin/core/browser/signin_switches.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "components/signin/public/base/signin_switches.h" namespace signin { diff --git a/chromium/components/signin/core/browser/device_id_helper.h b/chromium/components/signin/public/base/device_id_helper.h index 8e9980c99ae..6bda97cd33e 100644 --- a/chromium/components/signin/core/browser/device_id_helper.h +++ b/chromium/components/signin/public/base/device_id_helper.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_CORE_BROWSER_DEVICE_ID_HELPER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ID_HELPER_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_DEVICE_ID_HELPER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_DEVICE_ID_HELPER_H_ #include <string> @@ -39,4 +39,4 @@ std::string GetOrCreateScopedDeviceId(PrefService* prefs); } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ID_HELPER_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_DEVICE_ID_HELPER_H_ diff --git a/chromium/components/signin/core/browser/device_id_helper_unittest.cc b/chromium/components/signin/public/base/device_id_helper_unittest.cc index 3d35da3908b..1a0e30038bd 100644 --- a/chromium/components/signin/core/browser/device_id_helper_unittest.cc +++ b/chromium/components/signin/public/base/device_id_helper_unittest.cc @@ -2,13 +2,12 @@ // 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/device_id_helper.h" +#include "components/signin/public/base/device_id_helper.h" #include <string> #include "build/build_config.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_pref_names.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,7 +23,8 @@ TEST(DeviceIdHelper, GenerateSigninScopedDeviceId) { TEST(DeviceIdHelper, RecreateSigninScopedDeviceId) { sync_preferences::TestingPrefServiceSyncable prefs; - ProfileOAuth2TokenService::RegisterProfilePrefs(prefs.registry()); + prefs.registry()->RegisterStringPref( + prefs::kGoogleServicesSigninScopedDeviceId, std::string()); ASSERT_TRUE( prefs.GetString(prefs::kGoogleServicesSigninScopedDeviceId).empty()); @@ -42,7 +42,9 @@ TEST(DeviceIdHelper, RecreateSigninScopedDeviceId) { TEST(DeviceIdHelper, GetOrCreateScopedDeviceId) { sync_preferences::TestingPrefServiceSyncable prefs; - ProfileOAuth2TokenService::RegisterProfilePrefs(prefs.registry()); + prefs.registry()->RegisterStringPref( + prefs::kGoogleServicesSigninScopedDeviceId, std::string()); + ASSERT_TRUE( prefs.GetString(prefs::kGoogleServicesSigninScopedDeviceId).empty()); diff --git a/chromium/components/signin/core/browser/list_accounts_test_utils.cc b/chromium/components/signin/public/base/list_accounts_test_utils.cc index a8a41b2e2e7..d63a72d102c 100644 --- a/chromium/components/signin/core/browser/list_accounts_test_utils.cc +++ b/chromium/components/signin/public/base/list_accounts_test_utils.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/core/browser/list_accounts_test_utils.h" +#include "components/signin/public/base/list_accounts_test_utils.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -23,13 +23,20 @@ void SetListAccountsResponseHttpNotFound( /*content=*/"", net::HTTP_NOT_FOUND); } -void SetListAccountsResponseWebLoginRequired( +void SetListAccountsResponseWithUnexpectedServiceResponse( TestURLLoaderFactory* test_url_loader_factory) { + std::string source = GaiaConstants::kChromeSource; + // Set response for first request that will lead to a one time retry request. + test_url_loader_factory->AddResponse( + GaiaUrls::GetInstance()->ListAccountsURLWithSource(source).spec(), ""); + + // Seconde request would have the source with the error as a suffix. test_url_loader_factory->AddResponse( GaiaUrls::GetInstance() - ->ListAccountsURLWithSource(GaiaConstants::kChromeSource) + ->ListAccountsURLWithSource(source + + GaiaConstants::kUnexpectedServiceResponse) .spec(), - "Info=WebLoginRequired"); + ""); } void SetListAccountsResponseWithParams( diff --git a/chromium/components/signin/core/browser/list_accounts_test_utils.h b/chromium/components/signin/public/base/list_accounts_test_utils.h index 9a5d897f0df..782c36402c2 100644 --- a/chromium/components/signin/core/browser/list_accounts_test_utils.h +++ b/chromium/components/signin/public/base/list_accounts_test_utils.h @@ -1,8 +1,8 @@ // 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_LIST_ACCOUNTS_TEST_UTILS_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_LIST_ACCOUNTS_TEST_UTILS_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_LIST_ACCOUNTS_TEST_UTILS_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_LIST_ACCOUNTS_TEST_UTILS_H_ #include <string> #include <vector> @@ -26,8 +26,9 @@ struct CookieParams { void SetListAccountsResponseHttpNotFound( network::TestURLLoaderFactory* test_url_loader_factory); -// Make ListAccounts call return Info=WebLoginRequired. -void SetListAccountsResponseWebLoginRequired( +// Make ListAccounts call return an unexpected service response that leads to +// a one time retry request. It also sets the response for the retry request. +void SetListAccountsResponseWithUnexpectedServiceResponse( network::TestURLLoaderFactory* test_url_loader_factory); // Make ListAccounts return a list of accounts based on the provided |params|. @@ -64,4 +65,4 @@ void SetListAccountsResponseTwoAccounts( } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_LIST_ACCOUNTS_TEST_UTILS_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_LIST_ACCOUNTS_TEST_UTILS_H_ diff --git a/chromium/components/signin/core/browser/signin_client.cc b/chromium/components/signin/public/base/signin_client.cc index b6741ad109a..8a358e0d2ef 100644 --- a/chromium/components/signin/core/browser/signin_client.cc +++ b/chromium/components/signin/public/base/signin_client.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/core/browser/signin_client.h" +#include "components/signin/public/base/signin_client.h" void SigninClient::PreSignOut( base::OnceCallback<void(SignoutDecision)> on_signout_decision_reached, diff --git a/chromium/components/signin/core/browser/signin_client.h b/chromium/components/signin/public/base/signin_client.h index 8587e23de6c..d5c7889000c 100644 --- a/chromium/components/signin/core/browser/signin_client.h +++ b/chromium/components/signin/public/base/signin_client.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_CORE_BROWSER_SIGNIN_CLIENT_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_CLIENT_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_CLIENT_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_CLIENT_H_ #include <memory> @@ -12,9 +12,8 @@ #include "base/time/time.h" #include "build/build_config.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/signin/core/browser/account_consistency_method.h" -#include "components/signin/core/browser/account_info.h" -#include "components/signin/core/browser/signin_metrics.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_metrics.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "url/gurl.h" @@ -30,7 +29,7 @@ class SharedURLLoaderFactory; namespace mojom { class CookieManager; } -} +} // namespace network // An interface that needs to be supplied to the Signin component by its // embedder. @@ -73,9 +72,6 @@ class SigninClient : public KeyedService { // For iOS, cookies should be cleaned up. virtual void PreGaiaLogout(base::OnceClosure callback); - virtual bool IsFirstRun() const = 0; - virtual base::Time GetInstallDate() = 0; - // Returns true if GAIA cookies are allowed in the content area. virtual bool AreSigninCookiesAllowed() = 0; @@ -101,4 +97,4 @@ class SigninClient : public KeyedService { virtual void SetReadyForDiceMigration(bool is_ready) {} }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_CLIENT_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_CLIENT_H_ diff --git a/chromium/components/signin/core/browser/signin_metrics.cc b/chromium/components/signin/public/base/signin_metrics.cc index 3827846b2f9..64a04cb7543 100644 --- a/chromium/components/signin/core/browser/signin_metrics.cc +++ b/chromium/components/signin/public/base/signin_metrics.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/core/browser/signin_metrics.h" +#include "components/signin/public/base/signin_metrics.h" #include <limits.h> @@ -707,25 +707,6 @@ void LogSigninAccountReconciliationDuration(base::TimeDelta duration, } } -void LogSigninProfile(bool is_first_run, base::Time install_date) { - // Track whether or not the user signed in during the first run of Chrome. - UMA_HISTOGRAM_BOOLEAN("Signin.DuringFirstRun", is_first_run); - - // Determine how much time passed since install when this profile was signed - // in. - base::TimeDelta elapsed_time = base::Time::Now() - install_date; - UMA_HISTOGRAM_COUNTS_1M("Signin.ElapsedTimeFromInstallToSignin", - elapsed_time.InMinutes()); -} - -void LogSigninAddAccount() { - // Account signin may fail for a wide variety of reasons. There is no - // explicit false, but one can compare this value with the various UI - // flows that lead to account sign-in, and deduce that the difference - // counts the failures. - UMA_HISTOGRAM_BOOLEAN("Signin.AddAccount", true); -} - void LogSignout(ProfileSignout source_metric, SignoutDelete delete_metric) { UMA_HISTOGRAM_ENUMERATION("Signin.SignoutProfile", source_metric, NUM_PROFILE_SIGNOUT_METRICS); @@ -742,13 +723,11 @@ void LogExternalCcResultFetches( UMA_HISTOGRAM_BOOLEAN("Signin.Reconciler.AllExternalCcResultCompleted", fetches_completed); if (fetches_completed) { - UMA_HISTOGRAM_TIMES( - "Signin.Reconciler.ExternalCcResultTime.Completed", - time_to_check_connections); + UMA_HISTOGRAM_TIMES("Signin.Reconciler.ExternalCcResultTime.Completed", + time_to_check_connections); } else { - UMA_HISTOGRAM_TIMES( - "Signin.Reconciler.ExternalCcResultTime.NotCompleted", - time_to_check_connections); + UMA_HISTOGRAM_TIMES("Signin.Reconciler.ExternalCcResultTime.NotCompleted", + time_to_check_connections); } } diff --git a/chromium/components/signin/core/browser/signin_metrics.h b/chromium/components/signin/public/base/signin_metrics.h index b63c45b997b..77fe702a4e5 100644 --- a/chromium/components/signin/core/browser/signin_metrics.h +++ b/chromium/components/signin/public/base/signin_metrics.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_CORE_BROWSER_SIGNIN_METRICS_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_METRICS_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_METRICS_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_METRICS_H_ #include <limits.h> @@ -28,7 +28,7 @@ enum DifferentPrimaryAccounts { // Track all the ways a profile can become signed out as a histogram. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.signin // GENERATED_JAVA_CLASS_NAME_OVERRIDE: SignoutReason -enum ProfileSignout { +enum ProfileSignout : int { // The value used within unit tests. SIGNOUT_TEST = 0, // The preference or policy controlling if signin is valid has changed. @@ -296,8 +296,8 @@ enum class SourceForRefreshTokenOperation { kTokenService_LoadCredentials, kSupervisedUser_InitSync, kInlineLoginHandler_Signin, - kSigninManager_ClearPrimaryAccount, - kSigninManager_LegacyPreDiceSigninFlow, + kPrimaryAccountManager_ClearAccount, + kPrimaryAccountManager_LegacyPreDiceSigninFlow, kUserMenu_RemoveAccount, kUserMenu_SignOutAllAccounts, kSettings_Signout, @@ -361,12 +361,6 @@ void RecordAccountsPerProfile(int total_number_accounts); void LogSigninAccountReconciliationDuration(base::TimeDelta duration, bool successful); -// Track a successful signin. -void LogSigninAddAccount(); - -// Track a successful signin of a profile. -void LogSigninProfile(bool is_first_run, base::Time install_date); - // Track a profile signout. void LogSignout(ProfileSignout source_metric, SignoutDelete delete_metric); @@ -435,4 +429,4 @@ void RecordSigninImpressionWithAccountUserActionForAccessPoint( } // namespace signin_metrics -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_METRICS_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_METRICS_H_ diff --git a/chromium/components/signin/core/browser/signin_metrics_unittest.cc b/chromium/components/signin/public/base/signin_metrics_unittest.cc index ec4cb749f40..3a56f94ef53 100644 --- a/chromium/components/signin/core/browser/signin_metrics_unittest.cc +++ b/chromium/components/signin/public/base/signin_metrics_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/core/browser/signin_metrics.h" +#include "components/signin/public/base/signin_metrics.h" #include <string> #include <vector> @@ -129,8 +129,8 @@ class SigninMetricsTest : public ::testing::Test { } static bool AccessPointSupportsPersonalizedPromo(AccessPoint access_point) { - return base::ContainsValue(kAccessPointsThatSupportPersonalizedPromos, - access_point); + return base::Contains(kAccessPointsThatSupportPersonalizedPromos, + access_point); } }; diff --git a/chromium/components/signin/core/browser/signin_pref_names.cc b/chromium/components/signin/public/base/signin_pref_names.cc index 75a77be466b..d613924ca51 100644 --- a/chromium/components/signin/core/browser/signin_pref_names.cc +++ b/chromium/components/signin/public/base/signin_pref_names.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/core/browser/signin_pref_names.h" +#include "components/signin/public/base/signin_pref_names.h" namespace prefs { @@ -90,10 +90,6 @@ const char kGoogleServicesUsernamePattern[] = const char kReverseAutologinRejectedEmailList[] = "reverse_autologin.rejected_email_list"; -// Int64 which tracks, as time from epoch, when last time the user signed in -// to the browser. -const char kSignedInTime[] = "signin.signedin_time"; - // Boolean indicating if this profile was signed in with information from a // credential provider. const char kSignedInWithCredentialProvider[] = diff --git a/chromium/components/signin/core/browser/signin_pref_names.h b/chromium/components/signin/public/base/signin_pref_names.h index 61391ea9f6b..01d8bab7e53 100644 --- a/chromium/components/signin/core/browser/signin_pref_names.h +++ b/chromium/components/signin/public/base/signin_pref_names.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_CORE_BROWSER_SIGNIN_PREF_NAMES_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_PREF_NAMES_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_PREF_NAMES_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_PREF_NAMES_H_ namespace prefs { @@ -25,7 +25,6 @@ extern const char kGoogleServicesUserAccountId[]; extern const char kGoogleServicesUsername[]; extern const char kGoogleServicesUsernamePattern[]; extern const char kReverseAutologinRejectedEmailList[]; -extern const char kSignedInTime[]; extern const char kSignedInWithCredentialProvider[]; extern const char kSigninAllowed[]; extern const char kTokenServiceDiceCompatible[]; @@ -34,4 +33,4 @@ extern const char kTokenServiceExcludedSecondaryAccounts[]; } // namespace prefs -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_PREF_NAMES_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_PREF_NAMES_H_ diff --git a/chromium/components/signin/core/browser/signin_switches.cc b/chromium/components/signin/public/base/signin_switches.cc index 7d99de9b7ea..fb905755f84 100644 --- a/chromium/components/signin/core/browser/signin_switches.cc +++ b/chromium/components/signin/public/base/signin_switches.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/core/browser/signin_switches.h" +#include "components/signin/public/base/signin_switches.h" namespace switches { diff --git a/chromium/components/signin/core/browser/signin_switches.h b/chromium/components/signin/public/base/signin_switches.h index 6c2752ebcaf..1397cbf4a40 100644 --- a/chromium/components/signin/core/browser/signin_switches.h +++ b/chromium/components/signin/public/base/signin_switches.h @@ -2,10 +2,10 @@ // 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_SIGNIN_SWITCHES_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_SWITCHES_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ -#include "components/signin/core/browser/signin_buildflags.h" +#include "components/signin/public/base/signin_buildflags.h" namespace switches { @@ -28,4 +28,4 @@ extern const char kAccountConsistencyDice[]; } // namespace switches -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_SWITCHES_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ diff --git a/chromium/components/signin/core/browser/test_signin_client.cc b/chromium/components/signin/public/base/test_signin_client.cc index 3a74d1ada5c..cfa57bc209c 100644 --- a/chromium/components/signin/core/browser/test_signin_client.cc +++ b/chromium/components/signin/public/base/test_signin_client.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/core/browser/test_signin_client.h" +#include "components/signin/public/base/test_signin_client.h" #include <memory> @@ -68,7 +68,9 @@ void TestSigninClient::OverrideTestUrlLoaderFactory( test_url_loader_factory_ = factory; } -std::string TestSigninClient::GetProductVersion() { return ""; } +std::string TestSigninClient::GetProductVersion() { + return ""; +} void TestSigninClient::SetNetworkCallsDelayed(bool value) { network_calls_delayed_ = value; @@ -80,14 +82,6 @@ void TestSigninClient::SetNetworkCallsDelayed(bool value) { } } -bool TestSigninClient::IsFirstRun() const { - return false; -} - -base::Time TestSigninClient::GetInstallDate() { - return base::Time::Now(); -} - bool TestSigninClient::AreSigninCookiesAllowed() { return are_signin_cookies_allowed_; } @@ -97,12 +91,10 @@ bool TestSigninClient::AreSigninCookiesDeletedOnExit() { } void TestSigninClient::AddContentSettingsObserver( - content_settings::Observer* observer) { -} + content_settings::Observer* observer) {} void TestSigninClient::RemoveContentSettingsObserver( - content_settings::Observer* observer) { -} + content_settings::Observer* observer) {} void TestSigninClient::DelayNetworkCall(base::OnceClosure callback) { if (network_calls_delayed_) { diff --git a/chromium/components/signin/core/browser/test_signin_client.h b/chromium/components/signin/public/base/test_signin_client.h index 2347b86a978..5ea2deeca75 100644 --- a/chromium/components/signin/core/browser/test_signin_client.h +++ b/chromium/components/signin/public/base/test_signin_client.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_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_TEST_SIGNIN_CLIENT_H_ +#define COMPONENTS_SIGNIN_PUBLIC_BASE_TEST_SIGNIN_CLIENT_H_ #include <memory> #include <string> @@ -14,7 +14,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "components/signin/core/browser/signin_client.h" +#include "components/signin/public/base/signin_client.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/public/mojom/cookie_manager.mojom.h" @@ -36,7 +36,7 @@ class TestSigninClient : public SigninClient { void DoFinalInit() override; - // Returns NULL. + // Returns nullptr. // NOTE: This should be changed to return a properly-initalized PrefService // once there is a unit test that requires it. PrefService* GetPrefs() override; @@ -80,9 +80,6 @@ class TestSigninClient : public SigninClient { // executed immediately. void SetNetworkCallsDelayed(bool value); - // SigninClient overrides: - bool IsFirstRun() const override; - base::Time GetInstallDate() override; bool AreSigninCookiesAllowed() override; bool AreSigninCookiesDeletedOnExit() override; void AddContentSettingsObserver( @@ -113,4 +110,4 @@ class TestSigninClient : public SigninClient { DISALLOW_COPY_AND_ASSIGN(TestSigninClient); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_BASE_TEST_SIGNIN_CLIENT_H_ diff --git a/chromium/components/signin/public/identity_manager/BUILD.gn b/chromium/components/signin/public/identity_manager/BUILD.gn new file mode 100644 index 00000000000..28717d1888c --- /dev/null +++ b/chromium/components/signin/public/identity_manager/BUILD.gn @@ -0,0 +1,133 @@ +# 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. + +source_set("identity_manager") { + sources = [ + "access_token_fetcher.cc", + "access_token_fetcher.h", + "access_token_info.cc", + "access_token_info.h", + "account_info.cc", + "account_info.h", + "accounts_cookie_mutator.h", + "accounts_in_cookie_jar_info.cc", + "accounts_in_cookie_jar_info.h", + "accounts_mutator.h", + "device_accounts_synchronizer.h", + "diagnostics_provider.h", + "identity_manager.cc", + "identity_manager.h", + "identity_manager_builder.cc", + "identity_manager_builder.h", + "identity_utils.cc", + "identity_utils.h", + "load_credentials_state.h", + "primary_account_access_token_fetcher.cc", + "primary_account_access_token_fetcher.h", + "primary_account_mutator.h", + "set_accounts_in_cookie_result.h", + "ubertoken_fetcher.cc", + "ubertoken_fetcher.h", + ] + + configs += [ "//build/config/compiler:wexit_time_destructors" ] + + public_deps = [ + "//base", + "//components/keyed_service/core", + "//components/signin/public/base", + "//components/signin/public/base:signin_buildflags", + "//google_apis", + "//services/identity/public/cpp:cpp_types", + "//ui/gfx", + ] + + deps = [ + "//components/image_fetcher/core", + "//components/prefs", + "//components/signin/internal/identity_manager", + "//components/signin/public/webdata", + "//services/network/public/cpp", + ] + + if (is_chromeos) { + deps += [ "//components/user_manager" ] + } + + if (is_ios) { + deps += [ "ios" ] + } + + allow_circular_includes_from = [ + # This target is a pair with internal/identity_manager. They always go + # together and include headers from each other. + "//components/signin/internal/identity_manager", + ] +} + +source_set("unit_tests") { + testonly = true + + sources = [ + "access_token_fetcher_unittest.cc", + "account_info_unittest.cc", + "accounts_cookie_mutator_unittest.cc", + "accounts_mutator_unittest.cc", + "diagnostics_provider_unittest.cc", + "identity_manager_unittest.cc", + "identity_test_environment_unittest.cc", + "identity_utils_unittest.cc", + "primary_account_access_token_fetcher_unittest.cc", + "primary_account_mutator_unittest.cc", + ] + + deps = [ + ":identity_manager", + ":test_support", + "//base", + "//base/test:test_support", + "//components/image_fetcher/core:test_support", + "//components/prefs", + "//components/prefs:test_support", + "//components/signin/internal/identity_manager", + "//components/signin/internal/identity_manager:test_support", + "//components/signin/public/base", + "//components/signin/public/base:test_support", + "//components/signin/public/identity_manager", + "//components/sync_preferences:test_support", + "//google_apis", + "//services/network:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} + +source_set("test_support") { + testonly = true + + sources = [ + "identity_test_environment.cc", + "identity_test_environment.h", + "identity_test_utils.cc", + "identity_test_utils.h", + "test_identity_manager_observer.cc", + "test_identity_manager_observer.h", + ] + + public_deps = [ + "//base", + "//components/signin/public/base", + "//components/signin/public/identity_manager", + "//google_apis", + ] + + deps = [ + "//components/image_fetcher/core:test_support", + "//components/signin/internal/identity_manager", + "//components/signin/internal/identity_manager:test_support", + "//components/signin/public/base:test_support", + "//components/sync_preferences:test_support", + "//testing/gtest", + ] +} diff --git a/chromium/components/signin/public/identity_manager/DEPS b/chromium/components/signin/public/identity_manager/DEPS new file mode 100644 index 00000000000..eada2e2a7b8 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/signin/internal", +] diff --git a/chromium/components/signin/public/identity_manager/README.md b/chromium/components/signin/public/identity_manager/README.md new file mode 100644 index 00000000000..5f68f505c4e --- /dev/null +++ b/chromium/components/signin/public/identity_manager/README.md @@ -0,0 +1,38 @@ +IdentityManager is the next-generation C++ API for interacting with Google +identity. + +Documentation on the mapping between usage of legacy signin +classes (notably PrimaryAccountManager and ProfileOAuth2TokenService) and usage +of IdentityManager is available here: + +https://docs.google.com/document/d/14f3qqkDM9IE4Ff_l6wuXvCMeHfSC9TxKezXTCyeaPUY/edit# + +A quick inline cheat sheet for developers migrating from usage of //components/ +signin and //google_apis/gaia: + +- "Primary account" in IdentityManager refers to what is called the + "authenticated account" in PrimaryAccountManager, i.e., the account that has + been blessed for sync by the user. +- "Unconsented primary account" is intuitively the browsing identity of the user + that we display to the user; despite its name, the user may or may not have + blessed this account for sync. In particular, whenever a primary account + exists, the unconsented primary account equals to the primary account. On + desktop platforms (excl. ChromeOS), if no primary account exists and there + exist any content-area accounts, it equals to the first signed-in content-area + account. In all other cases there is no unconsented primary account. +- PrimaryAccountTokenFetcher is the primary client-side interface for obtaining + access tokens for the primary account. In particular, it can take care of + waiting until the primary account is available. +- AccessTokenFetcher is the client-side interface for obtaining access tokens + for arbitrary accounts. +- IdentityTestEnvironment is the preferred test infrastructure for unittests + of production code that interacts with IdentityManager. It is suitable for + use in cases where neither the production code nor the unittest is interacting + with Profile (e.g., //components-level unittests). +- identity_test_utils.h provides lower-level test facilities for interacting + explicitly with IdentityManager and its dependencies (PrimaryAccountManager, + ProfileOAuth2TokenService). These facilities are the way to interact with + IdentityManager in unittest contexts where the production code and/or the + unittest are interacting with Profile (in particular, where the + IdentityManager instance with which the test is interacting must be + IdentityManagerFactory::GetForProfile(profile)). diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher.cc b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc new file mode 100644 index 00000000000..200770b2a40 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc @@ -0,0 +1,190 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/access_token_fetcher.h" + +#include <utility> + +#include "base/logging.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/public/identity_manager/access_token_info.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +namespace signin { + +AccessTokenFetcher::AccessTokenFetcher(const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode) + : AccessTokenFetcher(account_id, + oauth_consumer_name, + token_service, + /*url_loader_factory=*/nullptr, + scopes, + std::move(callback), + mode) {} + +AccessTokenFetcher::AccessTokenFetcher( + const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode) + : AccessTokenFetcher(account_id, + /*client_id=*/std::string(), + /*client_secret=*/std::string(), + oauth_consumer_name, + token_service, + std::move(url_loader_factory), + scopes, + std::move(callback), + mode) {} + +AccessTokenFetcher::AccessTokenFetcher(const CoreAccountId& account_id, + const std::string client_id, + const std::string client_secret, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode) + : AccessTokenFetcher(account_id, + client_id, + client_secret, + oauth_consumer_name, + token_service, + /*url_loader_factory=*/nullptr, + scopes, + std::move(callback), + mode) {} + +AccessTokenFetcher::AccessTokenFetcher( + const CoreAccountId& account_id, + const std::string client_id, + const std::string client_secret, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode) + : OAuth2AccessTokenManager::Consumer(oauth_consumer_name), + account_id_(account_id), + client_id_(client_id), + client_secret_(client_secret), + token_service_(token_service), + url_loader_factory_(std::move(url_loader_factory)), + scopes_(scopes), + mode_(mode), + callback_(std::move(callback)), + token_service_observer_(this) { + DCHECK(client_id_.empty() == client_secret_.empty()); + DCHECK(client_id_.empty() || !url_loader_factory); + + if (mode_ == Mode::kImmediate || IsRefreshTokenAvailable()) { + StartAccessTokenRequest(); + return; + } + + // Start observing the IdentityManager. This observer will be removed either + // when a refresh token is obtained and an access token request is started or + // when this object is destroyed. + token_service_observer_.Add(token_service_); +} + +AccessTokenFetcher::~AccessTokenFetcher() {} + +bool AccessTokenFetcher::IsRefreshTokenAvailable() const { + DCHECK_EQ(Mode::kWaitUntilRefreshTokenAvailable, mode_); + + return token_service_->RefreshTokenIsAvailable(account_id_); +} + +void AccessTokenFetcher::StartAccessTokenRequest() { + DCHECK(mode_ == Mode::kImmediate || IsRefreshTokenAvailable()); + + // By the time of starting an access token request, we should no longer be + // listening for signin-related events. + DCHECK(!token_service_observer_.IsObserving(token_service_)); + + // Note: We might get here even in cases where we know that there's no refresh + // token. We're requesting an access token anyway, so that the token service + // will generate an appropriate error code that we can return to the client. + DCHECK(!access_token_request_); + + // TODO(843510): Consider making the request to ProfileOAuth2TokenService + // asynchronously once there are no direct clients of PO2TS (i.e., PO2TS is + // used only by this class and IdentityManager). + if (!client_id_.empty()) { + // Setting both the client ID/secret and the URL loader factory is not + // currently supported. + access_token_request_ = token_service_->StartRequestForClient( + account_id_, client_id_, client_secret_, scopes_, this); + return; + } + + if (url_loader_factory_) { + access_token_request_ = token_service_->StartRequestWithContext( + account_id_, url_loader_factory_, scopes_, this); + return; + } + + access_token_request_ = + token_service_->StartRequest(account_id_, scopes_, this); +} + +void AccessTokenFetcher::OnRefreshTokenAvailable( + const CoreAccountId& account_id) { + DCHECK_EQ(Mode::kWaitUntilRefreshTokenAvailable, mode_); + + if (!IsRefreshTokenAvailable()) + return; + + token_service_observer_.Remove(token_service_); + + StartAccessTokenRequest(); +} + +void AccessTokenFetcher::OnGetTokenSuccess( + const OAuth2AccessTokenManager::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { + DCHECK_EQ(request, access_token_request_.get()); + std::unique_ptr<OAuth2AccessTokenManager::Request> request_deleter( + std::move(access_token_request_)); + + RunCallbackAndMaybeDie( + GoogleServiceAuthError::AuthErrorNone(), + AccessTokenInfo(token_response.access_token, + token_response.expiration_time, token_response.id_token)); + + // Potentially dead after the above invocation; nothing to do except return. +} + +void AccessTokenFetcher::OnGetTokenFailure( + const OAuth2AccessTokenManager::Request* request, + const GoogleServiceAuthError& error) { + DCHECK_EQ(request, access_token_request_.get()); + std::unique_ptr<OAuth2AccessTokenManager::Request> request_deleter( + std::move(access_token_request_)); + + RunCallbackAndMaybeDie(error, AccessTokenInfo()); + + // Potentially dead after the above invocation; nothing to do except return. +} + +void AccessTokenFetcher::RunCallbackAndMaybeDie( + GoogleServiceAuthError error, + AccessTokenInfo access_token_info) { + // Per the contract of this class, it is allowed for consumers to delete this + // object from within the callback that is run below. Hence, it is not safe to + // add any code below this call. + std::move(callback_).Run(std::move(error), std::move(access_token_info)); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher.h b/chromium/components/signin/public/identity_manager/access_token_fetcher.h new file mode 100644 index 00000000000..0ee97625939 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher.h @@ -0,0 +1,151 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_FETCHER_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/scoped_observer.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +#include "services/identity/public/cpp/scope_set.h" + +namespace network { +class SharedURLLoaderFactory; +} + +class GoogleServiceAuthError; +class ProfileOAuth2TokenService; + +namespace signin { +struct AccessTokenInfo; + +// Helper class to ease the task of obtaining an OAuth2 access token for a +// given account. +// May only be used on the UI thread. +class AccessTokenFetcher : public OAuth2TokenServiceObserver, + public OAuth2AccessTokenManager::Consumer { + public: + // Specifies how this instance should behave: + // |kImmediate|: Makes one-shot immediate request. + // |kWaitUntilRefreshTokenAvailable|: Waits for the account to have a refresh + // token before making the request. + // Note that using |kWaitUntilRefreshTokenAvailable| can result in waiting + // forever if the user is not signed in and doesn't sign in. + enum class Mode { kImmediate, kWaitUntilRefreshTokenAvailable }; + + // Callback for when a request completes (successful or not). On successful + // requests, |error| is NONE and |access_token_info| contains info of the + // obtained OAuth2 access token. On failed requests, |error| contains the + // actual error and |access_token_info| is empty. + // NOTE: At the time that this method is invoked, it is safe for the client to + // destroy the AccessTokenFetcher instance that is invoking this callback. + using TokenCallback = + base::OnceCallback<void(GoogleServiceAuthError error, + AccessTokenInfo access_token_info)>; + + // Instantiates a fetcher and immediately starts the process of obtaining an + // OAuth2 access token for |account_id| and |scopes|. The |callback| is called + // once the request completes (successful or not). If the AccessTokenFetcher + // is destroyed before the process completes, the callback is not called. + AccessTokenFetcher(const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode); + + // Instantiates a fetcher and immediately starts the process of obtaining an + // OAuth2 access token for |account_id| and |scopes|, allowing clients to pass + // a |url_loader_factory| of their choice. The |callback| is called + // once the request completes (successful or not). If the AccessTokenFetcher + // is destroyed before the process completes, the callback is not called. + AccessTokenFetcher( + const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode); + + // Instantiates a fetcher and immediately starts the process of obtaining an + // OAuth2 access token for |account_id| and |scopes| using both the + // |client_id| and |client_secret| to identify the OAuth client app. + AccessTokenFetcher(const CoreAccountId& account_id, + const std::string client_id, + const std::string client_secret, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode); + + ~AccessTokenFetcher() override; + + private: + AccessTokenFetcher( + const CoreAccountId& account_id, + const std::string client_id, + const std::string client_secret, + const std::string& oauth_consumer_name, + ProfileOAuth2TokenService* token_service, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + TokenCallback callback, + Mode mode); + + // Returns true iff a refresh token is available for |account_id_|. Should + // only be called in mode |kWaitUntilAvailable|. + bool IsRefreshTokenAvailable() const; + + void StartAccessTokenRequest(); + + // OAuth2TokenServiceObserver implementation. + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override; + + // OAuth2AccessTokenManager::Consumer implementation. + void OnGetTokenSuccess( + const OAuth2AccessTokenManager::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; + void OnGetTokenFailure(const OAuth2AccessTokenManager::Request* request, + const GoogleServiceAuthError& error) override; + + // Invokes |callback_| with (|error|, |access_token_info|). 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 assume that it is + // dead after invoking this method and must not run any more code. + void RunCallbackAndMaybeDie(GoogleServiceAuthError error, + AccessTokenInfo access_token_info); + + const CoreAccountId account_id_; + const std::string client_id_; + const std::string client_secret_; + ProfileOAuth2TokenService* token_service_; + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + const identity::ScopeSet scopes_; + const Mode mode_; + + // NOTE: This callback should only be invoked from |RunCallbackAndMaybeDie|, + // as invoking it has the potential to destroy this object per this class's + // contract. + TokenCallback callback_; + + ScopedObserver<ProfileOAuth2TokenService, AccessTokenFetcher> + token_service_observer_; + + std::unique_ptr<OAuth2AccessTokenManager::Request> access_token_request_; + + DISALLOW_COPY_AND_ASSIGN(AccessTokenFetcher); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_FETCHER_H_ diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher_unittest.cc b/chromium/components/signin/public/identity_manager/access_token_fetcher_unittest.cc new file mode 100644 index 00000000000..2de2b66224d --- /dev/null +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher_unittest.cc @@ -0,0 +1,612 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/access_token_fetcher.h" + +#include <utility> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_task_environment.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/access_token_info.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "google_apis/gaia/oauth2_access_token_consumer.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::MockCallback; +using sync_preferences::TestingPrefServiceSyncable; +using testing::_; +using testing::StrictMock; + +namespace signin { + +namespace { + +const char kTestGaiaId[] = "dummyId"; +const char kTestGaiaId2[] = "dummyId2"; +const char kTestEmail[] = "me@gmail.com"; +const char kTestEmail2[] = "me2@gmail.com"; + +// Used just to check that the id_token is passed along. +const char kIdTokenEmptyServices[] = + "dummy-header." + "eyAic2VydmljZXMiOiBbXSB9" // payload: { "services": [] } + ".dummy-signature"; +} // namespace + +class AccessTokenFetcherTest + : public testing::Test, + public OAuth2AccessTokenManager::DiagnosticsObserver { + public: + using TestTokenCallback = + StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>; + + AccessTokenFetcherTest() + : signin_client_(&pref_service_), + token_service_(&pref_service_), + access_token_info_("access token", + base::Time::Now() + base::TimeDelta::FromHours(1), + std::string(kIdTokenEmptyServices)) { + AccountTrackerService::RegisterPrefs(pref_service_.registry()); + + account_tracker_ = std::make_unique<AccountTrackerService>(); + account_tracker_->Initialize(&pref_service_, base::FilePath()); + + token_service_.AddAccessTokenDiagnosticsObserver(this); + } + + ~AccessTokenFetcherTest() override { + token_service_.RemoveAccessTokenDiagnosticsObserver(this); + } + + std::string AddAccount(const std::string& gaia_id, const std::string& email) { + account_tracker()->SeedAccountInfo(gaia_id, email); + return account_tracker()->FindAccountInfoByGaiaId(gaia_id).account_id; + } + + std::unique_ptr<AccessTokenFetcher> CreateFetcher( + const std::string& account_id, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) { + std::set<std::string> scopes{"scope"}; + return std::make_unique<AccessTokenFetcher>(account_id, "test_consumer", + &token_service_, scopes, + std::move(callback), mode); + } + + std::unique_ptr<AccessTokenFetcher> CreateFetcherWithURLLoaderFactory( + const std::string& account_id, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) { + std::set<std::string> scopes{"scope"}; + return std::make_unique<AccessTokenFetcher>( + account_id, "test_consumer", &token_service_, url_loader_factory, + scopes, std::move(callback), mode); + } + + AccountTrackerService* account_tracker() { return account_tracker_.get(); } + + FakeProfileOAuth2TokenService* token_service() { return &token_service_; } + + void set_on_access_token_request_callback(base::OnceClosure callback) { + on_access_token_request_callback_ = std::move(callback); + } + + // Returns an AccessTokenInfo with valid information that can be used for + // completing access token requests. + AccessTokenInfo access_token_info() { return access_token_info_; } + + private: + // OAuth2AccessTokenManager::DiagnosticsObserver: + void OnAccessTokenRequested( + const CoreAccountId& account_id, + const std::string& consumer_id, + const OAuth2AccessTokenManager::ScopeSet& scopes) override { + if (on_access_token_request_callback_) + std::move(on_access_token_request_callback_).Run(); + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + TestingPrefServiceSyncable pref_service_; + TestSigninClient signin_client_; + FakeProfileOAuth2TokenService token_service_; + AccessTokenInfo access_token_info_; + std::unique_ptr<AccountTrackerService> account_tracker_; + base::OnceClosure on_access_token_request_callback_; +}; + +TEST_F(AccessTokenFetcherTest, OneShotShouldCallBackOnFulfilledRequest) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // 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())); + + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, + WaitUntilAvailableShouldCallBackOnFulfilledRequest) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // Since the refresh token is already available, this should result in an + // immediate request for an access token. + auto fetcher = + CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kWaitUntilRefreshTokenAvailable); + + run_loop.Run(); + + // 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())); + + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, + WaitUntilAvailableShouldCallBackOnFulfilledRequestAfterTokenAvailable) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + + // Since the refresh token is not available yet, this should just start + // waiting for it. + auto fetcher = + CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kWaitUntilRefreshTokenAvailable); + + // Before the refresh token is available, the callback shouldn't get called. + EXPECT_CALL(callback, Run(_, _)).Times(0); + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); + + // Once the refresh token becomes available, we should get an access token + // request. + token_service()->UpdateCredentials(account_id, "refresh token"); + + run_loop.Run(); + + // 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())); + + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, + WaitUntilAvailableShouldIgnoreRefreshTokenForDifferentAccount) { + TestTokenCallback callback; + + MockCallback<base::OnceClosure> access_token_request_callback; + set_on_access_token_request_callback(access_token_request_callback.Get()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + std::string other_account_id = AddAccount(kTestGaiaId2, kTestEmail2); + + // Since the refresh token is not available yet, this should just start + // waiting for it. + auto fetcher = + CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kWaitUntilRefreshTokenAvailable); + + // A refresh token for a different account should make no difference. + EXPECT_CALL(callback, Run(_, _)).Times(0); + EXPECT_CALL(access_token_request_callback, Run()).Times(0); + token_service()->UpdateCredentials(other_account_id, "refresh token"); + + base::RunLoop().RunUntilIdle(); +} + +TEST_F(AccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // Destroy the fetcher before the access token request is fulfilled. + fetcher.reset(); + + // Now fulfilling the access token request should have no effect. + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, ReturnsErrorWhenAccountIsUnknown) { + TestTokenCallback callback; + + base::RunLoop run_loop; + + // Account not present -> we should get called back. + auto fetcher = CreateFetcher("dummy_account_id", callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + EXPECT_CALL(callback, + Run(GoogleServiceAuthError( + GoogleServiceAuthError::State::USER_NOT_SIGNED_UP), + AccessTokenInfo())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + run_loop.Run(); +} + +TEST_F(AccessTokenFetcherTest, ReturnsErrorWhenAccountHasNoRefreshToken) { + TestTokenCallback callback; + + base::RunLoop run_loop; + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + + // Account has no refresh token -> we should get called back. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + EXPECT_CALL(callback, + Run(GoogleServiceAuthError( + GoogleServiceAuthError::State::USER_NOT_SIGNED_UP), + AccessTokenInfo())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + run_loop.Run(); +} + +TEST_F(AccessTokenFetcherTest, CanceledAccessTokenRequest) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + base::RunLoop run_loop2; + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop2, &base::RunLoop::Quit)); + + // A canceled access token request should result in a callback. + token_service()->IssueErrorForAllPendingRequestsForAccount( + account_id, + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + run_loop2.Run(); +} + +TEST_F(AccessTokenFetcherTest, RefreshTokenRevoked) { + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + TestTokenCallback callback; + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // Revoke the refresh token, which should cancel all pending requests. The + // fetcher should *not* retry. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())); + token_service()->RevokeCredentials(account_id); +} + +TEST_F(AccessTokenFetcherTest, FailedAccessTokenRequest) { + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + TestTokenCallback callback; + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // We should immediately get called back with an empty access token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), + AccessTokenInfo())); + token_service()->IssueErrorForAllPendingRequestsForAccount( + account_id, + GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); +} + +TEST_F(AccessTokenFetcherTest, MultipleRequestsForSameAccountFulfilled) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // This should also result in a request for an access token. + TestTokenCallback callback2; + base::RunLoop run_loop2; + set_on_access_token_request_callback(run_loop2.QuitClosure()); + auto fetcher2 = CreateFetcher(account_id, callback2.Get(), + AccessTokenFetcher::Mode::kImmediate); + run_loop2.Run(); + + // Once the access token request is fulfilled, both requests should get + // called back with the access token. + EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + EXPECT_CALL(callback2, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, MultipleRequestsForDifferentAccountsFulfilled) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // Add a second account and request an access token for it. + std::string account_id2 = AddAccount(kTestGaiaId2, kTestEmail2); + token_service()->UpdateCredentials(account_id2, "refresh token"); + TestTokenCallback callback2; + base::RunLoop run_loop2; + set_on_access_token_request_callback(run_loop2.QuitClosure()); + auto fetcher2 = CreateFetcher(account_id2, callback2.Get(), + AccessTokenFetcher::Mode::kImmediate); + run_loop2.Run(); + + // Once the first access token request is fulfilled, it should get + // called back with the access token. + EXPECT_CALL(callback, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); + + // Once the second access token request is fulfilled, it should get + // called back with the access token. + EXPECT_CALL(callback2, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + token_service()->IssueAllTokensForAccount( + account_id2, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +TEST_F(AccessTokenFetcherTest, + MultipleRequestsForDifferentAccountsCanceledAndFulfilled) { + TestTokenCallback callback; + + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + // This should result in a request for an access token. + auto fetcher = CreateFetcher(account_id, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + run_loop.Run(); + + // Add a second account and request an access token for it. + std::string account_id2 = AddAccount(kTestGaiaId2, kTestEmail2); + token_service()->UpdateCredentials(account_id2, "refresh token"); + + base::RunLoop run_loop2; + set_on_access_token_request_callback(run_loop2.QuitClosure()); + + TestTokenCallback callback2; + auto fetcher2 = CreateFetcher(account_id2, callback2.Get(), + AccessTokenFetcher::Mode::kImmediate); + run_loop2.Run(); + + // Cancel the first access token request: This should result in a callback + // for the first fetcher. + base::RunLoop run_loop3; + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop3, &base::RunLoop::Quit)); + + token_service()->IssueErrorForAllPendingRequestsForAccount( + account_id, + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + run_loop3.Run(); + + // Once the second access token request is fulfilled, it should get + // called back with the access token. + base::RunLoop run_loop4; + EXPECT_CALL(callback2, + Run(GoogleServiceAuthError::AuthErrorNone(), access_token_info())) + .WillOnce(testing::InvokeWithoutArgs(&run_loop4, &base::RunLoop::Quit)); + token_service()->IssueAllTokensForAccount( + account_id2, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); + + run_loop4.Run(); +} + +TEST_F(AccessTokenFetcherTest, FetcherWithCustomURLLoaderFactory) { + base::RunLoop run_loop; + set_on_access_token_request_callback(run_loop.QuitClosure()); + + std::string account_id = AddAccount(kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(account_id, "refresh token"); + + network::TestURLLoaderFactory test_url_loader_factory; + scoped_refptr<network::SharedURLLoaderFactory> test_shared_url_loader_factory( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory)); + + // This should result in a request for an access token. + TestTokenCallback callback; + auto fetcher = CreateFetcherWithURLLoaderFactory( + account_id, test_shared_url_loader_factory, callback.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // The URLLoaderFactory present in the pending request should match + // the one we specified when creating the AccessTokenFetcher. + std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests = + token_service()->GetPendingRequests(); + + EXPECT_EQ(pending_requests.size(), 1U); + EXPECT_EQ(pending_requests[0].url_loader_factory, + test_shared_url_loader_factory); + + // 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())); + + token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); + + // Now add a second account and request an access token for it to test + // that the default URLLoaderFactory is used if none is specified. + base::RunLoop run_loop2; + TestTokenCallback callback2; + + set_on_access_token_request_callback(run_loop2.QuitClosure()); + std::string account_id2 = AddAccount(kTestGaiaId2, kTestEmail2); + token_service()->UpdateCredentials(account_id2, "refresh token"); + + // CreateFetcher will create an AccessTokenFetcher without specifying + // any URLLoaderFactory, so that the default one will be used. + auto fetcher2 = CreateFetcher(account_id2, callback2.Get(), + AccessTokenFetcher::Mode::kImmediate); + + run_loop2.Run(); + + // There should be one pending request in this case too. + std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests2 = + token_service()->GetPendingRequests(); + EXPECT_EQ(pending_requests2.size(), 1U); + + // The URLLoaderFactory present in the pending request should match + // the one created by default for the token service's delegate. + ProfileOAuth2TokenServiceDelegate* service_delegate = + token_service()->GetDelegate(); + EXPECT_EQ(pending_requests2[0].url_loader_factory, + service_delegate->GetURLLoaderFactory()); + + // Check that everything worked as expected in this case as well. + EXPECT_CALL(callback2, Run(GoogleServiceAuthError::AuthErrorNone(), + access_token_info())); + token_service()->IssueAllTokensForAccount( + account_id2, + OAuth2AccessTokenConsumer::TokenResponse( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token)); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/access_token_info.cc b/chromium/components/signin/public/identity_manager/access_token_info.cc new file mode 100644 index 00000000000..8b377ad28b4 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/access_token_info.cc @@ -0,0 +1,15 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/access_token_info.h" + +namespace signin { + +bool operator==(const AccessTokenInfo& lhs, const AccessTokenInfo& rhs) { + return (lhs.token == rhs.token) && + (lhs.expiration_time == rhs.expiration_time) && + (lhs.id_token == rhs.id_token); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/access_token_info.h b/chromium/components/signin/public/identity_manager/access_token_info.h new file mode 100644 index 00000000000..974a15fd97a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/access_token_info.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_INFO_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_INFO_H_ + +#include <string> + +#include "base/macros.h" +#include "base/time/time.h" + +namespace signin { + +// Container for a valid access token plus associated metadata. +struct AccessTokenInfo { + // The access token itself. + std::string token; + + // The time at which this access token will expire. + base::Time expiration_time; + + // Contains extra information regarding the user's currently registered + // services. It is uncommon for consumers to need to interact with this field. + // To interact with it, first parse it via gaia::ParseServiceFlags(). + std::string id_token; + + AccessTokenInfo() = default; + AccessTokenInfo(const std::string& token_param, + const base::Time& expiration_time_param, + const std::string& id_token) + : token(token_param), + expiration_time(expiration_time_param), + id_token(id_token) {} +}; + +// Defined for testing purposes only. +bool operator==(const AccessTokenInfo& lhs, const AccessTokenInfo& rhs); + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCESS_TOKEN_INFO_H_ diff --git a/chromium/components/signin/core/browser/account_info.cc b/chromium/components/signin/public/identity_manager/account_info.cc index 3c164ea34ab..afe459f9f91 100644 --- a/chromium/components/signin/core/browser/account_info.cc +++ b/chromium/components/signin/public/identity_manager/account_info.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/core/browser/account_info.h" +#include "components/signin/public/identity_manager/account_info.h" #include "google_apis/gaia/gaia_auth_util.h" namespace { @@ -119,10 +119,3 @@ std::ostream& operator<<(std::ostream& os, const CoreAccountInfo& account) { << account.is_under_advanced_protection; return os; } - -AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info) { - if (account_info.email.empty() || account_info.gaia.empty()) - return EmptyAccountId(); - - return AccountId::FromUserEmailGaiaId(account_info.email, account_info.gaia); -} diff --git a/chromium/components/signin/core/browser/account_info.h b/chromium/components/signin/public/identity_manager/account_info.h index ca47ac7fd82..e8d5a61a74e 100644 --- a/chromium/components/signin/core/browser/account_info.h +++ b/chromium/components/signin/public/identity_manager/account_info.h @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_INFO_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_INFO_H_ #include <string> -#include "components/account_id/account_id.h" #include "google_apis/gaia/core_account_id.h" #include "ui/gfx/image/image.h" @@ -75,7 +74,4 @@ bool operator==(const CoreAccountInfo& l, const CoreAccountInfo& r); bool operator!=(const CoreAccountInfo& l, const CoreAccountInfo& r); std::ostream& operator<<(std::ostream& os, const CoreAccountInfo& account); -// Returns AccountID populated from |account_info|. -AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info); - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNT_INFO_H_ diff --git a/chromium/components/signin/core/browser/account_info_unittest.cc b/chromium/components/signin/public/identity_manager/account_info_unittest.cc index 2329095b45a..b265a5d2ade 100644 --- a/chromium/components/signin/core/browser/account_info_unittest.cc +++ b/chromium/components/signin/public/identity_manager/account_info_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/core/browser/account_info.h" +#include "components/signin/public/identity_manager/account_info.h" #include "testing/gtest/include/gtest/gtest.h" class AccountInfoTest : public testing::Test {}; diff --git a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h new file mode 100644 index 00000000000..af5dd83e6f7 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h @@ -0,0 +1,75 @@ +// 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_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" + +struct CoreAccountId; +class GoogleServiceAuthError; + +namespace signin { + +enum class SetAccountsInCookieResult; + +// AccountsCookieMutator is the interface to support merging known local Google +// accounts into the cookie jar tracking the list of logged-in Google sessions. +class AccountsCookieMutator { + public: + AccountsCookieMutator() = default; + virtual ~AccountsCookieMutator() = default; + + typedef base::OnceCallback<void(const CoreAccountId& account_id, + const GoogleServiceAuthError& error)> + AddAccountToCookieCompletedCallback; + + // Adds an account identified by |account_id| to the cookie responsible for + // tracking the list of logged-in Google sessions across the web. + virtual void AddAccountToCookie( + const CoreAccountId& account_id, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) = 0; + + // Adds an account identified by |account_id| and with |access_token| to the + // cookie responsible for tracking the list of logged-in Google sessions + // across the web. + virtual void AddAccountToCookieWithToken( + const CoreAccountId& account_id, + const std::string& access_token, + gaia::GaiaSource source, + AddAccountToCookieCompletedCallback completion_callback) = 0; + + // Updates the state of the Gaia cookie to contain |account_ids|, including + // removal of any accounts that are currently present in the cookie but not + // contained in |account_ids|. |set_accounts_in_cookies_completed_callback| + // will be invoked with the result of the operation: if the error is equal to + // GoogleServiceAuthError::AuthErrorNone() then the operation succeeded. + // Notably, if there are accounts being added for which IdentityManager does + // not have refresh tokens, the operation will fail with a + // GoogleServiceAuthError::USER_NOT_SIGNED_UP error. + virtual void SetAccountsInCookie( + const std::vector<CoreAccountId>& account_ids, + gaia::GaiaSource source, + 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; + + // Remove all accounts from the Gaia cookie. + virtual void LogOutAllAccounts(gaia::GaiaSource source) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutator); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_COOKIE_MUTATOR_H_ 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 new file mode 100644 index 00000000000..add100d356e --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc @@ -0,0 +1,422 @@ +// 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/public/identity_manager/accounts_cookie_mutator.h" + +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/test/bind_test_util.h" +#include "base/test/gtest_util.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/public/base/list_accounts_test_utils.h" +#include "components/signin/public/base/test_signin_client.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_environment.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" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#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_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kTestUnavailableAccountId[] = "unavailable_account_id"; +const char kTestOtherUnavailableAccountId[] = "other_unavailable_account_id"; +const char kTestAccountEmail[] = "test_user@test.com"; +const char kTestOtherAccountEmail[] = "test_other_user@test.com"; +const char kTestAccountGaiaId[] = "gaia_id_for_test_user_test.com"; +const char kTestAccessToken[] = "access_token"; +const char kTestUberToken[] = "test_uber_token"; +const char kTestOAuthMultiLoginResponse[] = R"( + { "status": "OK", + "cookies":[ + { + "name":"CookieName", + "value":"CookieValue", + "domain":".google.com", + "path":"/" + } + ] + })"; + +enum class AccountsCookiesMutatorAction { + kAddAccountToCookie, + kSetAccountsInCookie, + kTriggerCookieJarUpdateNoAccounts, + kTriggerCookieJarUpdateOneAccount, +}; + +} // namespace + +namespace signin { +class AccountsCookieMutatorTest : public testing::Test { + public: + AccountsCookieMutatorTest() + : test_signin_client_(&prefs_), + identity_test_env_(/*test_url_loader_factory=*/nullptr, + &prefs_, + AccountConsistencyMethod::kDisabled, + &test_signin_client_) {} + + ~AccountsCookieMutatorTest() override {} + + // Make an account available and returns the account ID. + std::string AddAcountWithRefreshToken(const std::string& email) { + return identity_test_env_.MakeAccountAvailable(email).account_id; + } + + // Feed the TestURLLoaderFactory with the responses for the requests that will + // be created by UberTokenFetcher when mergin accounts into the cookie jar. + void PrepareURLLoaderResponsesForAction(AccountsCookiesMutatorAction action) { + switch (action) { + case AccountsCookiesMutatorAction::kAddAccountToCookie: + GetTestURLLoaderFactory()->AddResponse( + GaiaUrls::GetInstance() + ->oauth1_login_url() + .Resolve(base::StringPrintf("?source=%s&issueuberauth=1", + GaiaConstants::kChromeSource)) + .spec(), + kTestUberToken, net::HTTP_OK); + + GetTestURLLoaderFactory()->AddResponse( + GaiaUrls::GetInstance() + ->GetCheckConnectionInfoURLWithSource( + GaiaConstants::kChromeSource) + .spec(), + std::string(), net::HTTP_OK); + + GetTestURLLoaderFactory()->AddResponse( + GaiaUrls::GetInstance() + ->merge_session_url() + .Resolve(base::StringPrintf( + "?uberauth=%s&continue=http://www.google.com&source=%s", + kTestUberToken, GaiaConstants::kChromeSource)) + .spec(), + std::string(), net::HTTP_OK); + break; + case AccountsCookiesMutatorAction::kSetAccountsInCookie: + GetTestURLLoaderFactory()->AddResponse( + GaiaUrls::GetInstance() + ->oauth_multilogin_url() + .Resolve(base::StringPrintf("?source=%s", + GaiaConstants::kChromeSource)) + .spec(), + std::string(kTestOAuthMultiLoginResponse), net::HTTP_OK); + break; + case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts: + SetListAccountsResponseNoAccounts(GetTestURLLoaderFactory()); + break; + case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount: + SetListAccountsResponseOneAccount(kTestAccountEmail, kTestAccountGaiaId, + GetTestURLLoaderFactory()); + break; + } + } + + IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; } + + TestIdentityManagerObserver* identity_manager_observer() { + return identity_test_env_.identity_manager_observer(); + } + + AccountsCookieMutator* accounts_cookie_mutator() { + return identity_test_env_.identity_manager()->GetAccountsCookieMutator(); + } + + network::TestURLLoaderFactory* GetTestURLLoaderFactory() { + return test_signin_client_.GetTestURLLoaderFactory(); + } + + private: + base::test::ScopedTaskEnvironment task_environment_; + sync_preferences::TestingPrefServiceSyncable prefs_; + TestSigninClient test_signin_client_; + IdentityTestEnvironment identity_test_env_; + + DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorTest); +}; + +// Test that adding a non existing account without providing an access token +// results in an error due to such account not being available. +TEST_F(AccountsCookieMutatorTest, AddAccountToCookie_NonExistingAccount) { + base::RunLoop run_loop; + std::string account_id_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_id_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + run_loop.Quit(); + }); + + accounts_cookie_mutator()->AddAccountToCookie(kTestUnavailableAccountId, + gaia::GaiaSource::kChrome, + std::move(completion_callback)); + run_loop.Run(); + + EXPECT_EQ(account_id_from_add_account_to_cookie_completed_callback, + kTestUnavailableAccountId); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback.state(), + GoogleServiceAuthError::USER_NOT_SIGNED_UP); +} + +// Test that adding an already available account without providing an access +// token results in such account being successfully merged into the cookie jar. +TEST_F(AccountsCookieMutatorTest, AddAccountToCookie_ExistingAccount) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kAddAccountToCookie); + // Adding an account with refresh token will trigger a cookie jar update. + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + std::string account_id = AddAcountWithRefreshToken(kTestAccountEmail); + base::RunLoop run_loop; + std::string account_id_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_id_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + run_loop.Quit(); + }); + + accounts_cookie_mutator()->AddAccountToCookie( + account_id, gaia::GaiaSource::kChrome, std::move(completion_callback)); + + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + account_id, kTestAccessToken, + base::Time::Now() + base::TimeDelta::FromHours(1)); + run_loop.Run(); + + EXPECT_EQ(account_id_from_add_account_to_cookie_completed_callback, + account_id); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback.state(), + GoogleServiceAuthError::NONE); +} + +// Test that adding a non existing account along with an access token, results +// on such account being successfully merged into the cookie jar. +TEST_F(AccountsCookieMutatorTest, + AddAccountToCookieWithAccessToken_NonExistingAccount) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kAddAccountToCookie); + + base::RunLoop run_loop; + std::string account_id_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_id_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + run_loop.Quit(); + }); + + accounts_cookie_mutator()->AddAccountToCookieWithToken( + kTestUnavailableAccountId, kTestAccessToken, gaia::GaiaSource::kChrome, + std::move(completion_callback)); + run_loop.Run(); + + EXPECT_EQ(account_id_from_add_account_to_cookie_completed_callback, + kTestUnavailableAccountId); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback.state(), + GoogleServiceAuthError::NONE); +} + +// Test that adding an already available account along with an access token, +// results in such account being successfully merged into the cookie jar. +TEST_F(AccountsCookieMutatorTest, + AddAccountToCookieWithAccessToken_ExistingAccount) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kAddAccountToCookie); + // Adding an account with refresh token will trigger a cookie jar update. + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + std::string account_id = AddAcountWithRefreshToken(kTestAccountEmail); + base::RunLoop run_loop; + std::string account_id_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_id_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + run_loop.Quit(); + }); + + accounts_cookie_mutator()->AddAccountToCookieWithToken( + account_id, kTestAccessToken, gaia::GaiaSource::kChrome, + std::move(completion_callback)); + + run_loop.Run(); + + EXPECT_EQ(account_id_from_add_account_to_cookie_completed_callback, + account_id); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback.state(), + GoogleServiceAuthError::NONE); +} + +// Test that trying to set a list of accounts in the cookie jar where none of +// those accounts have refresh tokens in IdentityManager results in an error. +TEST_F(AccountsCookieMutatorTest, SetAccountsInCookie_AllNonExistingAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kSetAccountsInCookie); + + base::RunLoop run_loop; + std::vector<CoreAccountId> accounts_ids = {kTestUnavailableAccountId, + kTestOtherUnavailableAccountId}; + accounts_cookie_mutator()->SetAccountsInCookie( + accounts_ids, gaia::GaiaSource::kChrome, + base::BindOnce( + [](base::OnceClosure quit_closure, SetAccountsInCookieResult result) { + EXPECT_EQ(result, SetAccountsInCookieResult::kPersistentError); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); + + run_loop.Run(); +} + +// Test that trying to set a list of accounts in the cookie jar where some of +// those accounts have no refresh tokens in IdentityManager results in an error. +TEST_F(AccountsCookieMutatorTest, SetAccountsInCookie_SomeNonExistingAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kSetAccountsInCookie); + // Adding an account with refresh token will trigger a cookie jar update. + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + std::string account_id = AddAcountWithRefreshToken(kTestAccountEmail); + base::RunLoop run_loop; + std::vector<CoreAccountId> accounts_ids = {account_id, + kTestUnavailableAccountId}; + accounts_cookie_mutator()->SetAccountsInCookie( + accounts_ids, gaia::GaiaSource::kChrome, + base::BindOnce( + [](base::OnceClosure quit_closure, SetAccountsInCookieResult result) { + EXPECT_EQ(result, SetAccountsInCookieResult::kPersistentError); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure())); + run_loop.Run(); +} + +// Test that trying to set a list of accounts in the cookie jar where all of +// those accounts have refresh tokens in IdentityManager results in them being +// successfully set. +TEST_F(AccountsCookieMutatorTest, SetAccountsInCookie_AllExistingAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kSetAccountsInCookie); + // Adding an account with refresh token will trigger a cookie jar update. + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + std::string account_id = AddAcountWithRefreshToken(kTestAccountEmail); + std::string other_account_id = + AddAcountWithRefreshToken(kTestOtherAccountEmail); + base::RunLoop run_loop; + std::vector<CoreAccountId> accounts_ids = {account_id, other_account_id}; + accounts_cookie_mutator()->SetAccountsInCookie( + accounts_ids, gaia::GaiaSource::kChrome, + 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 triggering the update of a cookie jar with no accounts works. +TEST_F(AccountsCookieMutatorTest, TriggerCookieJarUpdate_NoListedAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts); + + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + accounts_cookie_mutator()->TriggerCookieJarUpdate(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_EQ(accounts_in_jar_info.signed_in_accounts.size(), 0U); + EXPECT_EQ(accounts_in_jar_info.signed_out_accounts.size(), 0U); + EXPECT_TRUE(accounts_in_jar_info.accounts_are_fresh); + + EXPECT_EQ(identity_manager_observer() + ->ErrorFromAccountsInCookieUpdatedCallback() + .state(), + GoogleServiceAuthError::NONE); +} + +// Test triggering the update of a cookie jar with one accounts works and that +// the received accounts match the data injected via the TestURLLoaderFactory. +TEST_F(AccountsCookieMutatorTest, TriggerCookieJarUpdate_OneListedAccounts) { + PrepareURLLoaderResponsesForAction( + AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount); + + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + accounts_cookie_mutator()->TriggerCookieJarUpdate(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_EQ(accounts_in_jar_info.signed_in_accounts.size(), 1U); + EXPECT_EQ(accounts_in_jar_info.signed_in_accounts[0].gaia_id, + kTestAccountGaiaId); + EXPECT_EQ(accounts_in_jar_info.signed_in_accounts[0].email, + kTestAccountEmail); + + EXPECT_EQ(accounts_in_jar_info.signed_out_accounts.size(), 0U); + EXPECT_TRUE(accounts_in_jar_info.accounts_are_fresh); + + EXPECT_EQ(identity_manager_observer() + ->ErrorFromAccountsInCookieUpdatedCallback() + .state(), + GoogleServiceAuthError::NONE); +} + +// Test that trying to log out all sessions generates the right network request. +TEST_F(AccountsCookieMutatorTest, LogOutAllAccounts) { + 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); + run_loop.Run(); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.cc b/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.cc new file mode 100644 index 00000000000..840c204c5da --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.cc @@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" + +namespace signin { + +AccountsInCookieJarInfo::AccountsInCookieJarInfo() = default; + +AccountsInCookieJarInfo::AccountsInCookieJarInfo( + bool accounts_are_fresh_param, + const std::vector<gaia::ListedAccount>& signed_in_accounts_param, + const std::vector<gaia::ListedAccount>& signed_out_accounts_param) + : accounts_are_fresh(accounts_are_fresh_param), + signed_in_accounts(signed_in_accounts_param), + signed_out_accounts(signed_out_accounts_param) {} + +AccountsInCookieJarInfo::AccountsInCookieJarInfo( + const AccountsInCookieJarInfo& other) { + if (this == &other) + return; + accounts_are_fresh = other.accounts_are_fresh; + signed_in_accounts = other.signed_in_accounts; + signed_out_accounts = other.signed_out_accounts; +} + +AccountsInCookieJarInfo::~AccountsInCookieJarInfo() = default; + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.h b/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.h new file mode 100644 index 00000000000..1b54539d1b7 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_in_cookie_jar_info.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_IN_COOKIE_JAR_INFO_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_IN_COOKIE_JAR_INFO_H_ + +#include <vector> + +#include "google_apis/gaia/gaia_auth_util.h" + +namespace signin { + +// Container for a response to get the accounts in the cookie jar. +struct AccountsInCookieJarInfo { + // True if the accounts info from cookie is fresh and does not need to be + // updated. + bool accounts_are_fresh; + + // The current list of signed in accounts from the cookie jar. + std::vector<gaia::ListedAccount> signed_in_accounts; + + // The current list of signed out accounts from the cookie jar. + std::vector<gaia::ListedAccount> signed_out_accounts; + + AccountsInCookieJarInfo(); + AccountsInCookieJarInfo( + bool accounts_are_fresh_param, + const std::vector<gaia::ListedAccount>& signed_in_accounts_param, + const std::vector<gaia::ListedAccount>& signed_out_accounts_param); + AccountsInCookieJarInfo(const AccountsInCookieJarInfo& other); + ~AccountsInCookieJarInfo(); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_IN_COOKIE_JAR_INFO_H_ diff --git a/chromium/components/signin/public/identity_manager/accounts_mutator.h b/chromium/components/signin/public/identity_manager/accounts_mutator.h new file mode 100644 index 00000000000..eb55698778a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_mutator.h @@ -0,0 +1,82 @@ +// 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_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_H_ + +#include <string> + +#include "base/macros.h" +#include "base/optional.h" +#include "components/signin/public/base/signin_buildflags.h" + +namespace signin_metrics { +enum class SourceForRefreshTokenOperation; +} + +struct CoreAccountId; + +namespace signin { + +// AccountsMutator is the interface to support seeding of account info and +// mutation of refresh tokens for the user's Gaia accounts. +class AccountsMutator { + public: + AccountsMutator() = default; + virtual ~AccountsMutator() = default; + + // Updates the information of the account associated with |gaia_id|, first + // adding that account to the system if it is not known. + virtual CoreAccountId AddOrUpdateAccount( + const std::string& gaia_id, + const std::string& email, + const std::string& refresh_token, + bool is_under_advanced_protection, + signin_metrics::SourceForRefreshTokenOperation source) = 0; + + // Updates the information about account identified by |account_id|. + virtual void UpdateAccountInfo( + const CoreAccountId& account_id, + base::Optional<bool> is_child_account, + base::Optional<bool> is_under_advanced_protection) = 0; + + // Removes the account given by |account_id|. Also revokes the token + // server-side if needed. + virtual void RemoveAccount( + const CoreAccountId& account_id, + signin_metrics::SourceForRefreshTokenOperation source) = 0; + + // Removes all accounts. + virtual void RemoveAllAccounts( + signin_metrics::SourceForRefreshTokenOperation source) = 0; + + // Invalidates the refresh token of the primary account. + // The primary account must necessarily be set by the time this method + // is invoked. + virtual void InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation source) = 0; + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + // Removes the credentials associated to account_id from the internal storage, + // and moves them to |target|. The credentials are not revoked on the server, + // but the IdentityManager::Observer::OnRefreshTokenRemovedForAccount() + // notification is sent to the observers. Also recreates a new device ID for + // this mutator. The device ID of the current mutator is not moved to the + // target mutator. + virtual void MoveAccount(AccountsMutator* target, + const CoreAccountId& account_id) = 0; +#endif + + // Updates the refresh token for the supervised user. + // TODO(860492): Remove this once supervised user support is removed. + virtual void LegacySetRefreshTokenForSupervisedUser( + const std::string& refresh_token) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(AccountsMutator); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_ACCOUNTS_MUTATOR_H_ diff --git a/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc new file mode 100644 index 00000000000..98669f6dc8c --- /dev/null +++ b/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc @@ -0,0 +1,683 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/internal/identity_manager/accounts_mutator_impl.h" + +#include "base/bind.h" +#include "base/optional.h" +#include "base/test/gtest_util.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/public/base/device_id_helper.h" +#include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" +#include "components/signin/public/identity_manager/test_identity_manager_observer.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kTestGaiaId[] = "gaia-id-test_user@test.com"; +const char kTestGaiaId2[] = "gaia-id-test_user-2@test.com"; +const char kTestEmail[] = "test_user@test.com"; +const char kTestEmail2[] = "test_user@test-2.com"; +const char kRefreshToken[] = "refresh_token"; +const char kRefreshToken2[] = "refresh_token_2"; +const char kSupervisedUserPseudoEmail[] = "managed_user@localhost"; + +// Class that observes diagnostics updates from signin::IdentityManager. +class TestIdentityManagerDiagnosticsObserver + : public signin::IdentityManager::DiagnosticsObserver { + public: + explicit TestIdentityManagerDiagnosticsObserver( + signin::IdentityManager* identity_manager) + : identity_manager_(identity_manager) { + identity_manager_->AddDiagnosticsObserver(this); + } + ~TestIdentityManagerDiagnosticsObserver() override { + identity_manager_->RemoveDiagnosticsObserver(this); + } + + const std::string& token_updator_account_id() { + return token_updator_account_id_; + } + const std::string& token_updator_source() { return token_updator_source_; } + bool is_token_updator_refresh_token_valid() { + return is_token_updator_refresh_token_valid_; + } + const std::string& token_remover_account_id() { + return token_remover_account_id_; + } + const std::string& token_remover_source() { return token_remover_source_; } + + private: + // signin::IdentityManager::DiagnosticsObserver: + void OnRefreshTokenUpdatedForAccountFromSource( + const CoreAccountId& account_id, + bool is_refresh_token_valid, + const std::string& source) override { + token_updator_account_id_ = account_id; + is_token_updator_refresh_token_valid_ = is_refresh_token_valid; + token_updator_source_ = source; + } + + void OnRefreshTokenRemovedForAccountFromSource( + const CoreAccountId& account_id, + const std::string& source) override { + token_remover_account_id_ = account_id; + token_remover_source_ = source; + } + + signin::IdentityManager* identity_manager_; + std::string token_updator_account_id_; + std::string token_updator_source_; + std::string token_remover_account_id_; + std::string token_remover_source_; + bool is_token_updator_refresh_token_valid_; +}; + +} // namespace + +namespace signin { +class AccountsMutatorTest : public testing::Test { + public: + AccountsMutatorTest() + : identity_test_env_(&test_url_loader_factory_, &prefs_), + identity_manager_diagnostics_observer_(identity_manager()) {} + + ~AccountsMutatorTest() override {} + + PrefService* pref_service() { return &prefs_; } + + IdentityManager* identity_manager() { + return identity_test_env_.identity_manager(); + } + + TestIdentityManagerObserver* identity_manager_observer() { + return identity_test_env_.identity_manager_observer(); + } + + TestIdentityManagerDiagnosticsObserver* + identity_manager_diagnostics_observer() { + return &identity_manager_diagnostics_observer_; + } + + AccountsMutator* accounts_mutator() { + return identity_manager()->GetAccountsMutator(); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + sync_preferences::TestingPrefServiceSyncable prefs_; + network::TestURLLoaderFactory test_url_loader_factory_; + IdentityTestEnvironment identity_test_env_; + TestIdentityManagerDiagnosticsObserver identity_manager_diagnostics_observer_; + + DISALLOW_COPY_AND_ASSIGN(AccountsMutatorTest); +}; + +TEST_F(AccountsMutatorTest, Basic) { + // Should not crash. +} + +// Test that a new account gets added to the AccountTrackerService when calling +// AddOrUpdateAccount() and that a new refresh token becomes available for the +// passed account_id when adding an account for the first time. +TEST_F(AccountsMutatorTest, AddOrUpdateAccount_AddNewAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + + AccountInfo account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + EXPECT_EQ(account_info.account_id, account_id); + EXPECT_EQ(account_info.email, kTestEmail); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); +} + +// Test that no account gets added to the AccountTrackerService when calling +// AddOrUpdateAccount() if there's an account already tracked for a given id, +// and that its refresh token gets updated if a different one is passed. +TEST_F(AccountsMutatorTest, AddOrUpdateAccount_UpdateExistingAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // First of all add the account to the account tracker service. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + AccountInfo account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + EXPECT_EQ(account_info.account_id, account_id); + EXPECT_EQ(account_info.email, kTestEmail); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + + // Now try adding the account again with the same account id but with + // different information, and check that the account gets updated. + base::RunLoop run_loop2; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop2.QuitClosure()); + + // The internals of IdentityService is migrating from email to gaia id + // as the account id. Detect whether the current plaform has completed + // the migration. + const bool use_gaia_as_account_id = account_id == account_info.gaia; + + // If the system uses gaia id as account_id, then change the email and + // the |is_under_advanced_protection| field. Otherwise only change the + // latter. In all case, no new account should be created. + const char* const maybe_updated_email = + use_gaia_as_account_id ? kTestEmail2 : kTestEmail; + + accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, maybe_updated_email, kRefreshToken, + /*is_under_advanced_protection=*/true, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop2.Run(); + + EXPECT_EQ(identity_manager_observer() + ->AccountFromRefreshTokenUpdatedCallback() + .account_id, + account_id); + + // No new accounts should be created, just the information should be updated. + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + AccountInfo updated_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + EXPECT_EQ(account_info.account_id, updated_account_info.account_id); + EXPECT_EQ(account_info.gaia, updated_account_info.gaia); + EXPECT_EQ(updated_account_info.email, maybe_updated_email); + if (use_gaia_as_account_id) { + EXPECT_NE(updated_account_info.email, account_info.email); + EXPECT_EQ(updated_account_info.email, kTestEmail2); + } + EXPECT_NE(account_info.is_under_advanced_protection, + updated_account_info.is_under_advanced_protection); +} + +// Test that the information of an existing account for a given ID gets updated. +TEST_F(AccountsMutatorTest, UpdateAccountInfo) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // First of all add the account to the account tracker service. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + + AccountInfo original_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + EXPECT_EQ(original_account_info.account_id, account_id); + EXPECT_EQ(original_account_info.email, kTestEmail); + EXPECT_FALSE(original_account_info.is_child_account); + EXPECT_FALSE(original_account_info.is_under_advanced_protection); + + accounts_mutator()->UpdateAccountInfo( + account_id, + /*is_child_account=*/true, + /*is_under_advanced_protection=*/base::nullopt); + AccountInfo updated_account_info_1 = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + + // Only |is_child_account| changed so far, everything else remains the same. + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + EXPECT_EQ(updated_account_info_1.account_id, + original_account_info.account_id); + EXPECT_EQ(updated_account_info_1.email, original_account_info.email); + EXPECT_NE(updated_account_info_1.is_child_account, + original_account_info.is_child_account); + EXPECT_EQ(updated_account_info_1.is_under_advanced_protection, + original_account_info.is_under_advanced_protection); + + accounts_mutator()->UpdateAccountInfo(account_id, + /*is_child_account=*/base::nullopt, + /*is_under_advanced_protection=*/true); + AccountInfo updated_account_info_2 = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + + // |is_under_advanced_protection| has changed now, but |is_child_account| + // remains the same since we previously set it to |true| in the previous step. + EXPECT_NE(updated_account_info_2.is_under_advanced_protection, + original_account_info.is_under_advanced_protection); + EXPECT_EQ(updated_account_info_2.is_child_account, + updated_account_info_1.is_child_account); + + // Last, reset |is_child_account| and |is_under_advanced_protection| together + // to its initial |false| value, which is no longer the case. + EXPECT_TRUE(updated_account_info_2.is_child_account); + EXPECT_TRUE(updated_account_info_2.is_under_advanced_protection); + + accounts_mutator()->UpdateAccountInfo(account_id, + /*is_child_account=*/false, + /*is_under_advanced_protection=*/false); + AccountInfo reset_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + + // Everything is back to its original state now. + EXPECT_EQ(reset_account_info.is_child_account, + original_account_info.is_child_account); + EXPECT_EQ(reset_account_info.is_under_advanced_protection, + original_account_info.is_under_advanced_protection); + EXPECT_FALSE(reset_account_info.is_child_account); + EXPECT_FALSE(reset_account_info.is_under_advanced_protection); +} + +TEST_F(AccountsMutatorTest, + InvalidateRefreshTokenForPrimaryAccount_WithPrimaryAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // Set up the primary account. + std::string primary_account_email("primary.account@example.com"); + AccountInfo primary_account_info = + MakePrimaryAccountAvailable(identity_manager(), primary_account_email); + + // Now try invalidating the primary account, and check that it gets updated. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + accounts_mutator()->InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_EQ(identity_manager_observer() + ->AccountFromRefreshTokenUpdatedCallback() + .account_id, + primary_account_info.account_id); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_info.account_id)); + auto error = identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_info.account_id); + EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error.state()); + EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_CLIENT, + error.GetInvalidGaiaCredentialsReason()); +} + +TEST_F( + AccountsMutatorTest, + InvalidateRefreshTokenForPrimaryAccount_WithPrimaryAndSecondaryAccounts) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // Set up the primary account. + std::string primary_account_email("primary.account@example.com"); + AccountInfo primary_account_info = + MakePrimaryAccountAvailable(identity_manager(), primary_account_email); + + // Next, add a secondary account. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + AccountInfo secondary_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByAccountId(account_id) + .value(); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 2U); + + // Now try invalidating the primary account, and check that it gets updated. + base::RunLoop run_loop2; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop2.QuitClosure()); + + accounts_mutator()->InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop2.Run(); + + EXPECT_EQ(identity_manager_observer() + ->AccountFromRefreshTokenUpdatedCallback() + .account_id, + primary_account_info.account_id); + + // Check whether the primary account refresh token got invalidated. + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_info.account_id)); + auto error = identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_info.account_id); + EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error.state()); + EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_CLIENT, + error.GetInvalidGaiaCredentialsReason()); + + // Last, check whether the secondary account credentials remain untouched. + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + EXPECT_EQ(secondary_account_info.account_id, account_id); + EXPECT_EQ(secondary_account_info.email, kTestEmail); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 2U); +} + +TEST_F(AccountsMutatorTest, + InvalidateRefreshTokenForPrimaryAccount_WithoutPrimaryAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + EXPECT_FALSE(identity_manager()->HasPrimaryAccount()); + + // Now try invalidating the primary account, and make sure the test + // expectedly fails, since the primary account is not set. + EXPECT_DCHECK_DEATH( + accounts_mutator()->InvalidateRefreshTokenForPrimaryAccount( + signin_metrics::SourceForRefreshTokenOperation::kUnknown)); + + base::RunLoop().RunUntilIdle(); +} + +// Test that attempting to remove a non-existing account should not result in +// firing any callback from AccountTrackerService or ProfileOAuth2TokenService. +TEST_F(AccountsMutatorTest, RemoveAccount_NonExistingAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + base::BindOnce([]() { + // This callback should not be invoked now. + EXPECT_TRUE(false); + })); + + accounts_mutator()->RemoveAccount( + kTestGaiaId, signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.RunUntilIdle(); + + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(kTestGaiaId)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + kTestGaiaId)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 0U); +} + +// Test that attempting to remove an existing account should result in firing +// the right callbacks from AccountTrackerService or ProfileOAuth2TokenService. +TEST_F(AccountsMutatorTest, RemoveAccount_ExistingAccount) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // First of all add the account to the account tracker service. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + + // Now remove the account that we just added. + base::RunLoop run_loop2; + identity_manager_observer()->SetOnRefreshTokenRemovedCallback( + run_loop2.QuitClosure()); + + accounts_mutator()->RemoveAccount( + account_id, signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop2.Run(); + + EXPECT_EQ( + identity_manager_observer()->AccountIdFromRefreshTokenRemovedCallback(), + account_id); + + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 0U); +} + +// Test that attempting to remove all accounts removes all the tokens from the +// PO2TS and every account from the AccountTrackerService. +TEST_F(AccountsMutatorTest, RemoveAllAccounts) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // First of all the first account to the account tracker service. + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, kRefreshToken, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop.Run(); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 1U); + + // Now add the second account. + base::RunLoop run_loop2; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop2.QuitClosure()); + + std::string account_id2 = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId2, kTestEmail2, kRefreshToken2, + /*is_under_advanced_protection=*/false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop2.Run(); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_id2)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 2U); + + // Now remove everything and check that there are no lingering accounts, nor + // refresh tokens associated to |kTestGaiaId| and |kTestGaiaId2| afterwards. + base::RunLoop run_loop3; + accounts_mutator()->RemoveAllAccounts( + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + run_loop3.RunUntilIdle(); + + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(kTestGaiaId)); + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(kTestGaiaId2)); + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 0U); +} + +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +TEST_F(AccountsMutatorTest, MoveAccount) { + // All platforms that support DICE also support account mutation. + DCHECK(accounts_mutator()); + + AccountInfo account_info = + MakeAccountAvailable(identity_manager(), kTestEmail); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_info.account_id)); + EXPECT_EQ(1U, identity_manager()->GetAccountsWithRefreshTokens().size()); + + IdentityTestEnvironment other_identity_test_env; + auto* other_accounts_mutator = + other_identity_test_env.identity_manager()->GetAccountsMutator(); + + std::string device_id_1 = GetOrCreateScopedDeviceId(pref_service()); + EXPECT_FALSE(device_id_1.empty()); + + accounts_mutator()->MoveAccount(other_accounts_mutator, + account_info.account_id); + EXPECT_EQ(0U, identity_manager()->GetAccountsWithRefreshTokens().size()); + + std::string device_id_2 = GetOrCreateScopedDeviceId(pref_service()); + EXPECT_FALSE(device_id_2.empty()); + // |device_id_1| and |device_id_2| should be different as the divice ID is + // recreated in MoveAccount(). + EXPECT_NE(device_id_1, device_id_2); + + auto other_accounts_with_refresh_token = + other_identity_test_env.identity_manager() + ->GetAccountsWithRefreshTokens(); + EXPECT_EQ(1U, other_accounts_with_refresh_token.size()); + EXPECT_TRUE( + other_identity_test_env.identity_manager()->HasAccountWithRefreshToken( + other_accounts_with_refresh_token[0].account_id)); + EXPECT_FALSE(other_identity_test_env.identity_manager() + ->HasAccountWithRefreshTokenInPersistentErrorState( + other_accounts_with_refresh_token[0].account_id)); +} +#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) + +TEST_F(AccountsMutatorTest, LegacySetRefreshTokenForSupervisedUser) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + EXPECT_EQ(identity_manager()->GetAccountsWithRefreshTokens().size(), 0U); + + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + accounts_mutator()->LegacySetRefreshTokenForSupervisedUser(kRefreshToken); + run_loop.Run(); + + // In the context of supervised users, the ProfileOAuth2TokenService is used + // without the AccountTrackerService being used, so we can't use any of the + // IdentityManager::FindAccountInfoForAccountWithRefreshTokenBy*() methods + // since they won't find any account. Use GetAccountsWithRefreshTokens() and + // HasAccountWithRefreshToken*() instead, that only relies in the PO2TS. + std::vector<CoreAccountInfo> accounts = + identity_manager()->GetAccountsWithRefreshTokens(); + EXPECT_EQ(accounts.size(), 1U); + EXPECT_EQ(accounts[0].account_id, kSupervisedUserPseudoEmail); + EXPECT_EQ(accounts[0].email, kSupervisedUserPseudoEmail); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(accounts[0].account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + accounts[0].account_id)); +} + +TEST_F(AccountsMutatorTest, UpdateAccessTokenFromSource) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // Add a default account. + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, "refresh_token", false, + signin_metrics::SourceForRefreshTokenOperation::kUnknown); + EXPECT_EQ( + account_id, + identity_manager_diagnostics_observer()->token_updator_account_id()); + EXPECT_TRUE(identity_manager_diagnostics_observer() + ->is_token_updator_refresh_token_valid()); + EXPECT_EQ("Unknown", + identity_manager_diagnostics_observer()->token_updator_source()); + + // Update the default account with different source. + accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, "refresh_token2", true, + signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout); + EXPECT_EQ( + account_id, + identity_manager_diagnostics_observer()->token_updator_account_id()); + EXPECT_TRUE(identity_manager_diagnostics_observer() + ->is_token_updator_refresh_token_valid()); + EXPECT_EQ("Settings::Signout", + identity_manager_diagnostics_observer()->token_updator_source()); +} + +TEST_F(AccountsMutatorTest, RemoveRefreshTokenFromSource) { + // Abort the test if the current platform does not support accounts mutation. + if (!accounts_mutator()) + return; + + // Add a default account. + std::string account_id = accounts_mutator()->AddOrUpdateAccount( + kTestGaiaId, kTestEmail, "refresh_token", false, + signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout); + + // Remove the default account. + accounts_mutator()->RemoveAccount( + kTestGaiaId, + signin_metrics::SourceForRefreshTokenOperation::kSettings_Signout); + EXPECT_EQ("Settings::Signout", + identity_manager_diagnostics_observer()->token_remover_source()); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h b/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h new file mode 100644 index 00000000000..bf99d7cb3cd --- /dev/null +++ b/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h @@ -0,0 +1,41 @@ +// 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_PUBLIC_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_H_ + +#include "google_apis/gaia/core_account_id.h" + +namespace signin { + +// DeviceAccountsSynchronizer is the interface to support seeding the accounts +// information from a device-level store. +class DeviceAccountsSynchronizer { + public: + DeviceAccountsSynchronizer() = default; + virtual ~DeviceAccountsSynchronizer() = default; + + // Reloads the information of all device-level accounts. All device-level + // accounts will be visible in IdentityManager::GetAccountsWithRefreshTokens() + // with any persistent errors cleared after this method is called. + virtual void ReloadAllAccountsFromSystem() = 0; + + // Reloads the information of the device-level account with |account_id|. The + // account will be visible in IdentityManager::GetAccountsWithRefreshTokens() + // with any persistent error cleared after this method is called. + virtual void ReloadAccountFromSystem(const CoreAccountId& account_id) = 0; + + // Class is non-copyable, non-moveable. + DeviceAccountsSynchronizer(const DeviceAccountsSynchronizer&) = delete; + DeviceAccountsSynchronizer& operator=(const DeviceAccountsSynchronizer&) = + delete; + + DeviceAccountsSynchronizer(DeviceAccountsSynchronizer&&) noexcept = delete; + DeviceAccountsSynchronizer& operator=(DeviceAccountsSynchronizer&&) noexcept = + delete; +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_H_ diff --git a/chromium/components/signin/public/identity_manager/diagnostics_provider.h b/chromium/components/signin/public/identity_manager/diagnostics_provider.h new file mode 100644 index 00000000000..37d0c09031b --- /dev/null +++ b/chromium/components/signin/public/identity_manager/diagnostics_provider.h @@ -0,0 +1,39 @@ +// 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_PUBLIC_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_H_ + +#include "base/macros.h" +#include "base/time/time.h" +#include "components/signin/public/identity_manager/load_credentials_state.h" + +namespace signin { + +// DiagnosticsProvider is the interface to obtain diagnostics about +// IdentityManager internals. +class DiagnosticsProvider { + public: + DiagnosticsProvider() = default; + virtual ~DiagnosticsProvider() = default; + + // Returns the state of the load credentials operation. + virtual signin::LoadCredentialsState + GetDetailedStateOfLoadingOfRefreshTokens() const = 0; + + // Returns the time until a access token request can be sent (will be zero if + // the release time is in the past). + virtual base::TimeDelta GetDelayBeforeMakingAccessTokenRequests() const = 0; + + // Returns the time until a cookie request can be sent (will be zero if the + // release time is in the past). + virtual base::TimeDelta GetDelayBeforeMakingCookieRequests() const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(DiagnosticsProvider); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DIAGNOSTICS_PROVIDER_H_ diff --git a/chromium/components/signin/public/identity_manager/diagnostics_provider_unittest.cc b/chromium/components/signin/public/identity_manager/diagnostics_provider_unittest.cc new file mode 100644 index 00000000000..87fb50f9310 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/diagnostics_provider_unittest.cc @@ -0,0 +1,80 @@ +// 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/internal/identity_manager/diagnostics_provider_impl.h" + +#include "base/bind_helpers.h" +#include "base/macros.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "components/signin/public/identity_manager/load_credentials_state.h" +#include "testing/gtest/include/gtest/gtest.h" + +const char kAccountId[] = "user@gmail.com"; + +namespace { + +class DiagnosticsProviderTest : public testing::Test { + public: + DiagnosticsProviderTest() = default; + + signin::IdentityTestEnvironment* identity_test_env() { + return &identity_test_env_; + } + + signin::DiagnosticsProvider* diagnostics_provider() { + return identity_test_env_.identity_manager()->GetDiagnosticsProvider(); + } + + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + + private: + signin::IdentityTestEnvironment identity_test_env_; + + DISALLOW_COPY_AND_ASSIGN(DiagnosticsProviderTest); +}; + +} // namespace + +TEST_F(DiagnosticsProviderTest, Basic) { + // Accessing the DiagnosticProvider should not crash. + ASSERT_TRUE(identity_test_env()->identity_manager()); + EXPECT_TRUE( + identity_test_env()->identity_manager()->GetDiagnosticsProvider()); +} + +TEST_F(DiagnosticsProviderTest, GetDetailedStateOfLoadingOfRefreshTokens) { + EXPECT_EQ( + signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + diagnostics_provider()->GetDetailedStateOfLoadingOfRefreshTokens()); +} + +TEST_F(DiagnosticsProviderTest, GetDelayBeforeMakingAccessTokenRequests) { + base::TimeDelta zero; + EXPECT_EQ(diagnostics_provider()->GetDelayBeforeMakingAccessTokenRequests(), + zero); + std::string account_id = + identity_test_env()->MakeAccountAvailable(kAccountId).account_id; + identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount( + account_id, GoogleServiceAuthError( + GoogleServiceAuthError::State::SERVICE_UNAVAILABLE)); + EXPECT_GT(diagnostics_provider()->GetDelayBeforeMakingAccessTokenRequests(), + zero); +} + +TEST_F(DiagnosticsProviderTest, GetDelayBeforeMakingCookieRequests) { + base::TimeDelta zero; + identity_test_env() + ->identity_manager() + ->GetAccountsCookieMutator() + ->AddAccountToCookie(kAccountId, gaia::GaiaSource::kChrome, + base::DoNothing()); + EXPECT_EQ(diagnostics_provider()->GetDelayBeforeMakingCookieRequests(), zero); + + identity_test_env()->SimulateMergeSessionFailure( + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + EXPECT_GT(diagnostics_provider()->GetDelayBeforeMakingCookieRequests(), zero); +} diff --git a/chromium/components/signin/public/identity_manager/identity_manager.cc b/chromium/components/signin/public/identity_manager/identity_manager.cc new file mode 100644 index 00000000000..de180ab04ae --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_manager.cc @@ -0,0 +1,705 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_manager.h" + +#include <string> + +#include "base/bind.h" +#include "build/build_config.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/ubertoken_fetcher_impl.h" +#include "components/signin/public/identity_manager/accounts_cookie_mutator.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/accounts_mutator.h" +#include "components/signin/public/identity_manager/device_accounts_synchronizer.h" +#include "components/signin/public/identity_manager/diagnostics_provider.h" +#include "components/signin/public/identity_manager/primary_account_mutator.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +#if defined(OS_ANDROID) +#include "components/signin/internal/identity_manager/oauth2_token_service_delegate_android.h" +#elif !defined(OS_IOS) +#include "components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.h" +#endif + +namespace signin { + +namespace { + +// Local copy of the account ID used for supervised users (defined in //chrome +// as supervised_users::kSupervisedUserPseudoEmail). Simply copied to avoid +// plumbing it from //chrome all the way down through the Identity Service just +// to handle the corner cases below. +// TODO(860492): Remove this once supervised user support is removed. +const char kSupervisedUserPseudoEmail[] = "managed_user@localhost"; + +// A made-up Gaia ID to populate the supervised user's AccountInfo with in order +// to maintain the invariant that the AccountInfos passed out by IdentityManager +// always have an account ID, Gaia ID, and email set. +// TODO(860492): Remove this once supervised user support is removed. +const char kSupervisedUserPseudoGaiaID[] = "managed_user_gaia_id"; + +} // namespace + +IdentityManager::IdentityManager( + std::unique_ptr<AccountTrackerService> account_tracker_service, + std::unique_ptr<ProfileOAuth2TokenService> token_service, + std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, + std::unique_ptr<PrimaryAccountManager> primary_account_manager, + std::unique_ptr<AccountFetcherService> account_fetcher_service, + std::unique_ptr<PrimaryAccountMutator> primary_account_mutator, + std::unique_ptr<AccountsMutator> accounts_mutator, + std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator, + std::unique_ptr<DiagnosticsProvider> diagnostics_provider, + std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer) + : account_tracker_service_(std::move(account_tracker_service)), + token_service_(std::move(token_service)), + gaia_cookie_manager_service_(std::move(gaia_cookie_manager_service)), + primary_account_manager_(std::move(primary_account_manager)), + account_fetcher_service_(std::move(account_fetcher_service)), + primary_account_mutator_(std::move(primary_account_mutator)), + accounts_mutator_(std::move(accounts_mutator)), + accounts_cookie_mutator_(std::move(accounts_cookie_mutator)), + diagnostics_provider_(std::move(diagnostics_provider)), + device_accounts_synchronizer_(std::move(device_accounts_synchronizer)) { + DCHECK(account_fetcher_service_); + DCHECK(accounts_cookie_mutator_); + DCHECK(diagnostics_provider_); + + DCHECK(!accounts_mutator_ || !device_accounts_synchronizer_) + << "Cannot have both an AccountsMutator and a DeviceAccountsSynchronizer"; + + // IdentityManager will outlive the PrimaryAccountManager, so base::Unretained + // is safe. + primary_account_manager_->SetGoogleSigninSucceededCallback( + base::BindRepeating(&IdentityManager::GoogleSigninSucceeded, + base::Unretained(this))); + primary_account_manager_->SetAuthenticatedAccountSetCallback( + base::BindRepeating(&IdentityManager::AuthenticatedAccountSet, + base::Unretained(this))); + primary_account_manager_->SetAuthenticatedAccountClearedCallback( + base::BindRepeating(&IdentityManager::AuthenticatedAccountCleared, + base::Unretained(this))); +#if !defined(OS_CHROMEOS) + primary_account_manager_->SetGoogleSignedOutCallback(base::BindRepeating( + &IdentityManager::GoogleSignedOut, base::Unretained(this))); +#endif + + token_service_->AddObserver(this); + token_service_->AddAccessTokenDiagnosticsObserver(this); + + // IdentityManager owns the ATS, GCMS and PO2TS instances and will outlive + // them, so base::Unretained is safe. + account_tracker_service_->SetOnAccountUpdatedCallback(base::BindRepeating( + &IdentityManager::OnAccountUpdated, base::Unretained(this))); + account_tracker_service_->SetOnAccountRemovedCallback(base::BindRepeating( + &IdentityManager::OnAccountRemoved, base::Unretained(this))); + gaia_cookie_manager_service_->SetGaiaAccountsInCookieUpdatedCallback( + base::BindRepeating(&IdentityManager::OnGaiaAccountsInCookieUpdated, + base::Unretained(this))); + gaia_cookie_manager_service_->SetGaiaCookieDeletedByUserActionCallback( + base::BindRepeating(&IdentityManager::OnGaiaCookieDeletedByUserAction, + base::Unretained(this))); + token_service_->SetRefreshTokenAvailableFromSourceCallback( + base::BindRepeating(&IdentityManager::OnRefreshTokenAvailableFromSource, + base::Unretained(this))); + token_service_->SetRefreshTokenRevokedFromSourceCallback( + base::BindRepeating(&IdentityManager::OnRefreshTokenRevokedFromSource, + base::Unretained(this))); + + // Seed the primary account with any state that |primary_account_manager_| + // loaded from prefs. + if (primary_account_manager_->IsAuthenticated()) { + CoreAccountInfo account = + primary_account_manager_->GetAuthenticatedAccountInfo(); + DCHECK(!account.account_id.empty()); + SetPrimaryAccountInternal(std::move(account)); + } +} + +IdentityManager::~IdentityManager() { + account_fetcher_service_->Shutdown(); + gaia_cookie_manager_service_->Shutdown(); + token_service_->Shutdown(); + account_tracker_service_->Shutdown(); + + token_service_->RemoveObserver(this); + token_service_->RemoveAccessTokenDiagnosticsObserver(this); +} + +void IdentityManager::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void IdentityManager::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +// TODO(862619) change return type to base::Optional<CoreAccountInfo> +CoreAccountInfo IdentityManager::GetPrimaryAccountInfo() const { + DCHECK_EQ(primary_account_.has_value(), + primary_account_manager_->IsAuthenticated()); + auto result = primary_account_.value_or(CoreAccountInfo()); + DCHECK_EQ(result.account_id, + primary_account_manager_->GetAuthenticatedAccountId()); +#if DCHECK_IS_ON() + CoreAccountInfo primary_account_manager_account = + primary_account_manager_->GetAuthenticatedAccountInfo(); + if (!primary_account_manager_account.account_id.empty()) { + DCHECK_EQ(primary_account_manager_account, result) + << "If primary_account_manager_'s account is set (account has a " + "refresh token), primary_account_ must have the same value."; + } +#endif + return result; +} + +CoreAccountId IdentityManager::GetPrimaryAccountId() const { + return GetPrimaryAccountInfo().account_id; +} + +bool IdentityManager::HasPrimaryAccount() const { + DCHECK_EQ(primary_account_.has_value(), + primary_account_manager_->IsAuthenticated()); + return primary_account_.has_value(); +} + +CoreAccountId IdentityManager::GetUnconsentedPrimaryAccountId() const { + return GetUnconsentedPrimaryAccountInfo().account_id; +} + +CoreAccountInfo IdentityManager::GetUnconsentedPrimaryAccountInfo() const { + return unconsented_primary_account_.value_or(CoreAccountInfo()); +} + +bool IdentityManager::HasUnconsentedPrimaryAccount() const { + return unconsented_primary_account_.has_value(); +} + +std::unique_ptr<AccessTokenFetcher> +IdentityManager::CreateAccessTokenFetcherForAccount( + const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) { + return std::make_unique<AccessTokenFetcher>(account_id, oauth_consumer_name, + token_service_.get(), scopes, + std::move(callback), mode); +} + +std::unique_ptr<AccessTokenFetcher> +IdentityManager::CreateAccessTokenFetcherForAccount( + const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) { + return std::make_unique<AccessTokenFetcher>( + account_id, oauth_consumer_name, token_service_.get(), url_loader_factory, + scopes, std::move(callback), mode); +} + +std::unique_ptr<AccessTokenFetcher> +IdentityManager::CreateAccessTokenFetcherForClient( + const CoreAccountId& account_id, + const std::string& client_id, + const std::string& client_secret, + const std::string& oauth_consumer_name, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) { + return std::make_unique<AccessTokenFetcher>( + account_id, client_id, client_secret, oauth_consumer_name, + token_service_.get(), scopes, std::move(callback), mode); +} + +void IdentityManager::RemoveAccessTokenFromCache( + const CoreAccountId& account_id, + const identity::ScopeSet& scopes, + const std::string& access_token) { + token_service_->InvalidateAccessToken(account_id, scopes, access_token); +} + +std::vector<CoreAccountInfo> IdentityManager::GetAccountsWithRefreshTokens() + const { + std::vector<CoreAccountId> account_ids_with_tokens = + token_service_->GetAccounts(); + + std::vector<CoreAccountInfo> accounts; + accounts.reserve(account_ids_with_tokens.size()); + + for (const CoreAccountId& account_id : account_ids_with_tokens) { + accounts.push_back(GetAccountInfoForAccountWithRefreshToken(account_id)); + } + + return accounts; +} + +std::vector<AccountInfo> +IdentityManager::GetExtendedAccountInfoForAccountsWithRefreshToken() const { + std::vector<CoreAccountId> account_ids_with_tokens = + token_service_->GetAccounts(); + + std::vector<AccountInfo> accounts; + accounts.reserve(account_ids_with_tokens.size()); + + for (const CoreAccountId& account_id : account_ids_with_tokens) { + accounts.push_back(GetAccountInfoForAccountWithRefreshToken(account_id)); + } + + return accounts; +} + +bool IdentityManager::HasPrimaryAccountWithRefreshToken() const { + return HasAccountWithRefreshToken(GetPrimaryAccountId()); +} + +bool IdentityManager::HasAccountWithRefreshToken( + const CoreAccountId& account_id) const { + return token_service_->RefreshTokenIsAvailable(account_id); +} + +bool IdentityManager::AreRefreshTokensLoaded() const { + return token_service_->AreAllCredentialsLoaded(); +} + +bool IdentityManager::HasAccountWithRefreshTokenInPersistentErrorState( + const CoreAccountId& account_id) const { + return GetErrorStateOfRefreshTokenForAccount(account_id).IsPersistentError(); +} + +GoogleServiceAuthError IdentityManager::GetErrorStateOfRefreshTokenForAccount( + const CoreAccountId& account_id) const { + return token_service_->GetAuthError(account_id); +} + +base::Optional<AccountInfo> IdentityManager::FindExtendedAccountInfoForAccount( + const CoreAccountInfo& account_info) const { + AccountInfo extended_account_info = + account_tracker_service_->GetAccountInfo(account_info.account_id); + + // AccountTrackerService always returns an AccountInfo, even on failure. In + // case of failure, the AccountInfo will be unpopulated, thus we should not + // be able to find a valid refresh token. + if (!HasAccountWithRefreshToken(extended_account_info.account_id)) + return base::nullopt; + + return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); +} + +base::Optional<AccountInfo> +IdentityManager::FindAccountInfoForAccountWithRefreshTokenByAccountId( + const CoreAccountId& account_id) const { + AccountInfo account_info = + account_tracker_service_->GetAccountInfo(account_id); + + // AccountTrackerService always returns an AccountInfo, even on failure. In + // case of failure, the AccountInfo will be unpopulated, thus we should not + // be able to find a valid refresh token. + if (!HasAccountWithRefreshToken(account_info.account_id)) + return base::nullopt; + + return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); +} + +base::Optional<AccountInfo> +IdentityManager::FindAccountInfoForAccountWithRefreshTokenByEmailAddress( + const std::string& email_address) const { + AccountInfo account_info = + account_tracker_service_->FindAccountInfoByEmail(email_address); + + // AccountTrackerService always returns an AccountInfo, even on failure. In + // case of failure, the AccountInfo will be unpopulated, thus we should not + // be able to find a valid refresh token. + if (!HasAccountWithRefreshToken(account_info.account_id)) + return base::nullopt; + + return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); +} + +base::Optional<AccountInfo> +IdentityManager::FindAccountInfoForAccountWithRefreshTokenByGaiaId( + const std::string& gaia_id) const { + AccountInfo account_info = + account_tracker_service_->FindAccountInfoByGaiaId(gaia_id); + + // AccountTrackerService always returns an AccountInfo, even on failure. In + // case of failure, the AccountInfo will be unpopulated, thus we should not + // be able to find a valid refresh token. + if (!HasAccountWithRefreshToken(account_info.account_id)) + return base::nullopt; + + return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); +} + +std::unique_ptr<UbertokenFetcher> +IdentityManager::CreateUbertokenFetcherForAccount( + const CoreAccountId& account_id, + UbertokenFetcher::CompletionCallback callback, + gaia::GaiaSource source, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + bool bount_to_channel_id) { + return std::make_unique<UbertokenFetcherImpl>( + account_id, token_service_.get(), std::move(callback), source, + url_loader_factory, bount_to_channel_id); +} + +AccountsInCookieJarInfo IdentityManager::GetAccountsInCookieJar() const { + std::vector<gaia::ListedAccount> signed_in_accounts; + std::vector<gaia::ListedAccount> signed_out_accounts; + bool accounts_are_fresh = gaia_cookie_manager_service_->ListAccounts( + &signed_in_accounts, &signed_out_accounts); + + return AccountsInCookieJarInfo(accounts_are_fresh, signed_in_accounts, + signed_out_accounts); +} + +PrimaryAccountMutator* IdentityManager::GetPrimaryAccountMutator() { + return primary_account_mutator_.get(); +} + +AccountsMutator* IdentityManager::GetAccountsMutator() { + return accounts_mutator_.get(); +} + +AccountsCookieMutator* IdentityManager::GetAccountsCookieMutator() { + return accounts_cookie_mutator_.get(); +} + +DeviceAccountsSynchronizer* IdentityManager::GetDeviceAccountsSynchronizer() { + return device_accounts_synchronizer_.get(); +} + +void IdentityManager::AddDiagnosticsObserver(DiagnosticsObserver* observer) { + diagnostics_observer_list_.AddObserver(observer); +} + +void IdentityManager::RemoveDiagnosticsObserver(DiagnosticsObserver* observer) { + diagnostics_observer_list_.RemoveObserver(observer); +} + +void IdentityManager::OnNetworkInitialized() { + gaia_cookie_manager_service_->InitCookieListener(); + account_fetcher_service_->OnNetworkInitialized(); +} + +// static +bool IdentityManager::IsAccountIdMigrationSupported() { + return AccountTrackerService::IsMigrationSupported(); +} + +// static +void IdentityManager::RegisterLocalStatePrefs(PrefRegistrySimple* registry) { + PrimaryAccountManager::RegisterPrefs(registry); +} + +// static +void IdentityManager::RegisterProfilePrefs(PrefRegistrySimple* registry) { + ProfileOAuth2TokenService::RegisterProfilePrefs(registry); + PrimaryAccountManager::RegisterProfilePrefs(registry); + AccountFetcherService::RegisterPrefs(registry); + AccountTrackerService::RegisterPrefs(registry); +#if !defined(OS_ANDROID) && !defined(OS_IOS) + MutableProfileOAuth2TokenServiceDelegate::RegisterProfilePrefs(registry); +#endif +} + +CoreAccountId IdentityManager::PickAccountIdForAccount( + const std::string& gaia, + const std::string& email) const { + // TODO(triploblastic@): Remove explicit conversion once + // primary_account_manager has been fixed to use CoreAccountId. + return CoreAccountId( + account_tracker_service_->PickAccountIdForAccount(gaia, email)); +} + +IdentityManager::AccountIdMigrationState +IdentityManager::GetAccountIdMigrationState() const { + return static_cast<IdentityManager::AccountIdMigrationState>( + account_tracker_service_->GetMigrationState()); +} + +#if !defined(OS_IOS) && !defined(OS_ANDROID) +void IdentityManager::DeprecatedLoadCredentialsForSupervisedUser( + const CoreAccountId& primary_account_id) { + token_service_->LoadCredentials(primary_account_id); +} +#endif + +DiagnosticsProvider* IdentityManager::GetDiagnosticsProvider() { + return diagnostics_provider_.get(); +} + +#if defined(OS_IOS) +void IdentityManager::ForceTriggerOnCookieChange() { + gaia_cookie_manager_service_->ForceOnCookieChangeProcessing(); +} +#endif + +#if defined(OS_ANDROID) +void IdentityManager::LegacyReloadAccountsFromSystem() { + token_service_->GetDelegate()->ReloadAccountsFromSystem( + GetPrimaryAccountId()); +} + +base::android::ScopedJavaLocalRef<jobject> +IdentityManager::LegacyGetAccountTrackerServiceJavaObject() { + return account_tracker_service_->GetJavaObject(); +} + +base::android::ScopedJavaLocalRef<jobject> +IdentityManager::LegacyGetOAuth2TokenServiceJavaObject() { + OAuth2TokenServiceDelegateAndroid* delegate = + static_cast<OAuth2TokenServiceDelegateAndroid*>( + token_service_->GetDelegate()); + return delegate->GetJavaObject(); +} + +void IdentityManager::ForceRefreshOfExtendedAccountInfo( + const CoreAccountId& account_id) { + DCHECK(HasAccountWithRefreshToken(account_id)); + account_fetcher_service_->ForceRefreshOfAccountInfo(account_id); +} +#endif + +PrimaryAccountManager* IdentityManager::GetPrimaryAccountManager() { + return primary_account_manager_.get(); +} + +ProfileOAuth2TokenService* IdentityManager::GetTokenService() { + return token_service_.get(); +} + +AccountTrackerService* IdentityManager::GetAccountTrackerService() { + return account_tracker_service_.get(); +} + +AccountFetcherService* IdentityManager::GetAccountFetcherService() { + return account_fetcher_service_.get(); +} + +GaiaCookieManagerService* IdentityManager::GetGaiaCookieManagerService() { + return gaia_cookie_manager_service_.get(); +} + +AccountInfo IdentityManager::GetAccountInfoForAccountWithRefreshToken( + const CoreAccountId& account_id) const { + // TODO(https://crbug.com/919793): This invariant is not currently possible to + // enforce on Android due to the underlying relationship between + // O2TS::GetAccounts(), O2TS::RefreshTokenIsAvailable(), and + // O2TS::Observer::OnRefreshTokenAvailable(). +#if !defined(OS_ANDROID) + DCHECK(HasAccountWithRefreshToken(account_id)); +#endif + + AccountInfo account_info = + account_tracker_service_->GetAccountInfo(account_id); + + // In the context of supervised users, the ProfileOAuth2TokenService is used + // without the AccountTrackerService being used. This is the only case in + // which the AccountTrackerService will potentially not know about the + // account. In this context, |account_id| is always set to + // kSupervisedUserPseudoEmail. Populate the information manually in this case + // to maintain the invariant that the account ID, gaia ID, and email are + // always set. + // TODO(860492): Remove this special case once supervised user support is + // removed. + DCHECK(!account_info.IsEmpty() || + account_id.id == kSupervisedUserPseudoEmail); + if (account_id.id == kSupervisedUserPseudoEmail && account_info.IsEmpty()) { + account_info.account_id = account_id; + account_info.email = kSupervisedUserPseudoEmail; + account_info.gaia = kSupervisedUserPseudoGaiaID; + } + + return account_info; +} + +void IdentityManager::SetPrimaryAccountInternal( + base::Optional<CoreAccountInfo> account_info) { + primary_account_ = std::move(account_info); + UpdateUnconsentedPrimaryAccount(); +} + +void IdentityManager::UpdateUnconsentedPrimaryAccount() { + base::Optional<CoreAccountInfo> new_unconsented_primary_account = + ComputeUnconsentedPrimaryAccountInfo(); + if (unconsented_primary_account_ != new_unconsented_primary_account) { + unconsented_primary_account_ = std::move(new_unconsented_primary_account); + for (auto& observer : observer_list_) { + observer.OnUnconsentedPrimaryAccountChanged( + unconsented_primary_account_.value_or(CoreAccountInfo())); + } + } +} + +base::Optional<CoreAccountInfo> +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 + // the unconsented primary account. By this early return, we avoid an extra + // request to GAIA that lists cookie accounts. + return base::nullopt; +#else + std::vector<gaia::ListedAccount> cookie_accounts = + GetAccountsInCookieJar().signed_in_accounts; + if (cookie_accounts.empty()) + return base::nullopt; + + const CoreAccountId first_account_id = cookie_accounts[0].id; + if (!HasAccountWithRefreshToken(first_account_id)) + return base::nullopt; + + return GetAccountInfoForAccountWithRefreshToken(first_account_id); +#endif +} + +void IdentityManager::GoogleSigninSucceeded(const AccountInfo& account_info) { + for (auto& observer : observer_list_) { + observer.OnPrimaryAccountSet(account_info); + } +} + +void IdentityManager::GoogleSignedOut(const AccountInfo& account_info) { + DCHECK(!HasPrimaryAccount()); + for (auto& observer : observer_list_) { + observer.OnPrimaryAccountCleared(account_info); + } +} +void IdentityManager::AuthenticatedAccountSet(const AccountInfo& account_info) { + DCHECK(primary_account_manager_->IsAuthenticated()); + SetPrimaryAccountInternal(account_info); +} +void IdentityManager::AuthenticatedAccountCleared() { + DCHECK(!primary_account_manager_->IsAuthenticated()); + SetPrimaryAccountInternal(base::nullopt); +} + +void IdentityManager::OnRefreshTokenAvailable(const CoreAccountId& account_id) { + UpdateUnconsentedPrimaryAccount(); + CoreAccountInfo account_info = + GetAccountInfoForAccountWithRefreshToken(account_id); + + for (auto& observer : observer_list_) { + observer.OnRefreshTokenUpdatedForAccount(account_info); + } +} + +void IdentityManager::OnRefreshTokenRevoked(const CoreAccountId& account_id) { + UpdateUnconsentedPrimaryAccount(); + for (auto& observer : observer_list_) { + observer.OnRefreshTokenRemovedForAccount(account_id); + } +} + +void IdentityManager::OnRefreshTokensLoaded() { + UpdateUnconsentedPrimaryAccount(); + for (auto& observer : observer_list_) + observer.OnRefreshTokensLoaded(); +} + +void IdentityManager::OnEndBatchChanges() { + for (auto& observer : observer_list_) + observer.OnEndBatchOfRefreshTokenStateChanges(); +} + +void IdentityManager::OnAuthErrorChanged( + const CoreAccountId& account_id, + const GoogleServiceAuthError& auth_error) { + CoreAccountInfo account_info = + GetAccountInfoForAccountWithRefreshToken(account_id); + + for (auto& observer : observer_list_) + observer.OnErrorStateOfRefreshTokenUpdatedForAccount(account_info, + auth_error); +} + +void IdentityManager::OnGaiaAccountsInCookieUpdated( + const std::vector<gaia::ListedAccount>& signed_in_accounts, + const std::vector<gaia::ListedAccount>& signed_out_accounts, + const GoogleServiceAuthError& error) { + UpdateUnconsentedPrimaryAccount(); + AccountsInCookieJarInfo accounts_in_cookie_jar_info( + error == GoogleServiceAuthError::AuthErrorNone(), signed_in_accounts, + signed_out_accounts); + + for (auto& observer : observer_list_) { + observer.OnAccountsInCookieUpdated(accounts_in_cookie_jar_info, error); + } +} + +void IdentityManager::OnGaiaCookieDeletedByUserAction() { + UpdateUnconsentedPrimaryAccount(); + for (auto& observer : observer_list_) { + observer.OnAccountsCookieDeletedByUserAction(); + } +} + +void IdentityManager::OnAccessTokenRequested(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::ScopeSet& scopes) { + for (auto& observer : diagnostics_observer_list_) { + observer.OnAccessTokenRequested(account_id, consumer_id, scopes); + } +} + +void IdentityManager::OnFetchAccessTokenComplete( + const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::ScopeSet& scopes, + GoogleServiceAuthError error, + base::Time expiration_time) { + for (auto& observer : diagnostics_observer_list_) + observer.OnAccessTokenRequestCompleted(account_id, consumer_id, scopes, + error, expiration_time); +} + +void IdentityManager::OnAccessTokenRemoved(const CoreAccountId& account_id, + const identity::ScopeSet& scopes) { + for (auto& observer : diagnostics_observer_list_) + observer.OnAccessTokenRemovedFromCache(account_id, scopes); +} + +void IdentityManager::OnRefreshTokenAvailableFromSource( + const CoreAccountId& account_id, + bool is_refresh_token_valid, + const std::string& source) { + for (auto& observer : diagnostics_observer_list_) + observer.OnRefreshTokenUpdatedForAccountFromSource( + account_id, is_refresh_token_valid, source); +} + +void IdentityManager::OnRefreshTokenRevokedFromSource( + const CoreAccountId& account_id, + const std::string& source) { + for (auto& observer : diagnostics_observer_list_) + observer.OnRefreshTokenRemovedForAccountFromSource(account_id, source); +} + +void IdentityManager::OnAccountUpdated(const AccountInfo& info) { + if (primary_account_ && primary_account_->account_id == info.account_id) { + SetPrimaryAccountInternal(info); + } + for (auto& observer : observer_list_) { + observer.OnExtendedAccountInfoUpdated(info); + } +} + +void IdentityManager::OnAccountRemoved(const AccountInfo& info) { + for (auto& observer : observer_list_) + observer.OnExtendedAccountInfoRemoved(info); + UpdateUnconsentedPrimaryAccount(); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_manager.h b/chromium/components/signin/public/identity_manager/identity_manager.h new file mode 100644 index 00000000000..00ad7000e59 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_manager.h @@ -0,0 +1,703 @@ +// 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_IDENTITY_MANAGER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_MANAGER_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "build/build_config.h" +#include "components/keyed_service/core/keyed_service.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/ubertoken_fetcher.h" +#include "google_apis/gaia/oauth2_access_token_manager.h" +#include "google_apis/gaia/oauth2_token_service_observer.h" +#include "services/identity/public/cpp/scope_set.h" + +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#endif + +namespace gaia { +class GaiaSource; +struct ListedAccount; +} // namespace gaia + +namespace network { +class SharedURLLoaderFactory; +class TestURLLoaderFactory; +} // namespace network + +// Necessary to declare these classes as friends. +class PrefRegistrySimple; +class SigninManagerAndroid; + +class AccountFetcherService; +class AccountTrackerService; +class GaiaCookieManagerService; +class PrimaryAccountManager; +class ProfileOAuth2TokenService; + +namespace signin { + +class AccountsMutator; +class AccountsCookieMutator; +struct AccountsInCookieJarInfo; +class IdentityManagerTest; +class IdentityTestEnvironment; +class DeviceAccountsSynchronizer; +class DiagnosticsProvider; +class PrimaryAccountMutator; +enum class ClearPrimaryAccountPolicy; +struct CookieParamsForTest; + +// Gives access to information about the user's Google identities. See +// ./README.md for detailed documentation. +class IdentityManager : public KeyedService, + public OAuth2AccessTokenManager::DiagnosticsObserver, + public OAuth2TokenServiceObserver { + public: + class Observer { + public: + Observer() = default; + virtual ~Observer() = default; + + Observer(const Observer&) = delete; + Observer& operator=(const Observer&) = delete; + + // Called when an account becomes the user's primary account. + // This method is not called during a reauth. + virtual void OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) {} + + // Called when the user moves from having a primary account to no longer + // having a primary account (note that the user may still have an + // *unconsented* primary account after this event; see./README.md). + virtual void OnPrimaryAccountCleared( + const CoreAccountInfo& previous_primary_account_info) {} + + // When the unconsented primary account (see ./README.md) of the user + // changes, this callback gets called with the new account as + // |unconsented_primary_account_info|. If after the change, there is no + // unconsented primary account, |unconsented_primary_account_info| is empty. + // This does not get called when the unconsented account becomes consented + // (as the same account was unconsented before so there is no change). In + // all other changes (the unconsented primary account gets added, changed or + // removed), this notification is called only once. Note: we do not use the + // {Set, Cleared} notifications like for the primary account above because + // the identity manager does not have clear guarantees that that account + // cannot change in one atomic operation (without getting cleared in the + // mean-time). + virtual void OnUnconsentedPrimaryAccountChanged( + const CoreAccountInfo& unconsented_primary_account_info) {} + + // Called when a new refresh token is associated with |account_info|. + // NOTE: On a signin event, the ordering of this callback wrt the + // OnPrimaryAccountSet() callback is undefined. If you as a client are + // interested in both callbacks, PrimaryAccountAccessTokenFetcher will + // likely meet your needs. Otherwise, if this lack of ordering is + // problematic for your use case, please contact blundell@chromium.org. + virtual void OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) {} + + // Called when the refresh token previously associated with |account_id| + // has been removed. At the time that this callback is invoked, there is + // no longer guaranteed to be any AccountInfo associated with + // |account_id|. + // NOTE: It is not guaranteed that a call to + // OnRefreshTokenUpdatedForAccount() has previously occurred for this + // account due to corner cases. + // TODO(https://crbug.com/884731): Eliminate these corner cases. + // NOTE: On a signout event, the ordering of this callback wrt the + // OnPrimaryAccountCleared() callback is undefined.If this lack of ordering + // is problematic for your use case, please contact blundell@chromium.org. + virtual void OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) {} + + // Called when the error state of the refresh token for |account_id| has + // changed. Note: It is always called after + // |OnRefreshTokenUpdatedForAccount| when the refresh token is updated. It + // is not called when the refresh token is removed. + virtual void OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info, + const GoogleServiceAuthError& error) {} + + // Called after refresh tokens are loaded. + virtual void OnRefreshTokensLoaded() {} + + // Called whenever the list of Gaia accounts in the cookie jar has changed. + // |accounts_in_cookie_jar_info.accounts| is ordered by the order of the + // accounts in the cookie. + // + // This observer method is also called when fetching the list of accounts + // in Gaia cookies fails after a number of internal retries. In this case: + // * |error| hold the last error to fetch the list of accounts; + // * |accounts_in_cookie_jar_info.accounts_are_fresh| is set to false as + // the accounts information is considered stale; + // * |accounts_in_cookie_jar_info.accounts| holds the last list of known + // accounts in the cookie jar. + virtual void OnAccountsInCookieUpdated( + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const GoogleServiceAuthError& error) {} + + // Called when the Gaia cookie has been deleted explicitly by a user + // action, e.g. from the settings or by an extension. + virtual void OnAccountsCookieDeletedByUserAction() {} + + // Called after a batch of refresh token state chagnes is completed. + virtual void OnEndBatchOfRefreshTokenStateChanges() {} + + // Called after an account is updated. + virtual void OnExtendedAccountInfoUpdated(const AccountInfo& info) {} + + // Called after removing an account info. + virtual void OnExtendedAccountInfoRemoved(const AccountInfo& info) {} + }; + + // Methods to register or remove observers. + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + // Provides access to the core information of the user's primary account. + // 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; + + // Provides access to the account ID of the user's primary account. Simple + // convenience wrapper over GetPrimaryAccountInfo().account_id. + CoreAccountId GetPrimaryAccountId() 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; + + // 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, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; + + // Creates an AccessTokenFetcher given the passed-in information, allowing + // to specify a custom |url_loader_factory| as well. + std::unique_ptr<AccessTokenFetcher> CreateAccessTokenFetcherForAccount( + const CoreAccountId& account_id, + const std::string& oauth_consumer_name, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; + + // Creates an AccessTokenFetcher given the passed-in information, allowing to + // specify custom |client_id| and |client_secret| to identify the OAuth client + // app. + std::unique_ptr<AccessTokenFetcher> CreateAccessTokenFetcherForClient( + const CoreAccountId& account_id, + const std::string& client_id, + const std::string& client_secret, + const std::string& oauth_consumer_name, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + AccessTokenFetcher::Mode mode) WARN_UNUSED_RESULT; + + // If an entry exists in the cache of access tokens corresponding to the + // given information, removes that entry; in this case, the next access token + // 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 std::string& access_token); + + // Provides the information of all accounts that have refresh tokens. + // NOTE: The accounts should not be assumed to be in any particular order; in + // particular, they are not guaranteed to be in the order in which the + // refresh tokens were added. + std::vector<CoreAccountInfo> GetAccountsWithRefreshTokens() const; + + // Same functionality as GetAccountsWithRefreshTokens() but returning the + // extended account information. + std::vector<AccountInfo> GetExtendedAccountInfoForAccountsWithRefreshToken() + const; + + // Returns true if (a) the primary account exists, and (b) a refresh token + // exists for the primary account. + bool HasPrimaryAccountWithRefreshToken() const; + + // Returns true if a refresh token exists for |account_id|. + bool HasAccountWithRefreshToken(const CoreAccountId& account_id) const; + + // Returns true if all refresh tokens have been loaded from disk. + bool AreRefreshTokensLoaded() const; + + // Returns true if (a) a refresh token exists for |account_id|, and (b) the + // refresh token is in a persistent error state (defined as + // GoogleServiceAuthError::IsPersistentError() returning true for the error + // returned by GetErrorStateOfRefreshTokenForAccount(account_id)). + bool HasAccountWithRefreshTokenInPersistentErrorState( + const CoreAccountId& account_id) const; + + // Returns the error state of the refresh token associated with |account_id|. + // In particular: Returns GoogleServiceAuthError::AuthErrorNone() if either + // (a) no refresh token exists for |account_id|, or (b) the refresh token is + // not in a persistent error state. Otherwise, returns the last persistent + // error that was detected when using the refresh token. + GoogleServiceAuthError GetErrorStateOfRefreshTokenForAccount( + const CoreAccountId& account_id) const; + + // Returns extended information for account identified by |account_info|. + // The information will be returned if the information is available and + // refresh token is available for account. + base::Optional<AccountInfo> FindExtendedAccountInfoForAccount( + const CoreAccountInfo& account_info) const; + + // Looks up and returns information for account with given |account_id|. If + // the account cannot be found, return an empty optional. This is equivalent + // to searching on the vector returned by GetAccountsWithRefreshTokens() but + // without allocating memory for the vector. + base::Optional<AccountInfo> + FindAccountInfoForAccountWithRefreshTokenByAccountId( + const CoreAccountId& account_id) const; + + // Looks up and returns information for account with given |email_address|. If + // the account cannot be found, return an empty optional. This is equivalent + // to searching on the vector returned by GetAccountsWithRefreshTokens() but + // without allocating memory for the vector. + base::Optional<AccountInfo> + FindAccountInfoForAccountWithRefreshTokenByEmailAddress( + const std::string& email_address) const; + + // Looks up and returns information for account with given |gaia_id|. If the + // account cannot be found, return an empty optional. This is equivalent to + // searching on the vector returned by GetAccountsWithRefreshTokens() but + // without allocating memory for the vector. + base::Optional<AccountInfo> FindAccountInfoForAccountWithRefreshTokenByGaiaId( + const std::string& gaia_id) const; + + // Creates an UbertokenFetcher given the passed-in information, allowing + // to specify a custom |url_loader_factory| as well. + std::unique_ptr<UbertokenFetcher> CreateUbertokenFetcherForAccount( + const CoreAccountId& account_id, + UbertokenFetcher::CompletionCallback callback, + gaia::GaiaSource source, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + bool bound_to_channel_id = true); + + // Provides the information of all accounts that are present in the Gaia + // cookie in the cookie jar, ordered by their order in the cookie. + // If the returned accounts are not fresh, an internal update will be + // triggered and there will be a subsequent invocation of + // IdentityManager::Observer::OnAccountsInCookieJarChanged(). + AccountsInCookieJarInfo GetAccountsInCookieJar() const; + + // Returns pointer to the object used to change the signed-in state of the + // primary account, if supported on the current platform. Otherwise, returns + // null. + PrimaryAccountMutator* GetPrimaryAccountMutator(); + + // Returns pointer to the object used to seed accounts and mutate state of + // accounts' refresh tokens, if supported on the current platform. Otherwise, + // returns null. + AccountsMutator* GetAccountsMutator(); + + // Returns pointer to the object used to manipulate the cookies stored and the + // accounts associated with them. Guaranteed to be non-null. + AccountsCookieMutator* GetAccountsCookieMutator(); + + // Returns pointer to the object used to seed accounts information from the + // device-level accounts. May be null if the system has no such notion. + DeviceAccountsSynchronizer* GetDeviceAccountsSynchronizer(); + + // Observer interface for classes that want to monitor status of various + // requests. Mostly useful in tests and debugging contexts (e.g., WebUI). + class DiagnosticsObserver { + public: + DiagnosticsObserver() = default; + virtual ~DiagnosticsObserver() = default; + + DiagnosticsObserver(const DiagnosticsObserver&) = delete; + DiagnosticsObserver& operator=(const DiagnosticsObserver&) = delete; + + // Called when receiving request for access token. + virtual void OnAccessTokenRequested(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::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, + GoogleServiceAuthError error, + base::Time expiration_time) {} + + // Called when an access token was removed. + virtual void OnAccessTokenRemovedFromCache( + const CoreAccountId& account_id, + const identity::ScopeSet& scopes) {} + + // Called when a new refresh token is available. Contains diagnostic + // information about the source of the operation. + virtual void OnRefreshTokenUpdatedForAccountFromSource( + const CoreAccountId& account_id, + bool is_refresh_token_valid, + const std::string& source) {} + + // Called when a refresh token is removed. Contains diagnostic information + // about the source that initiated the revokation operation. + virtual void OnRefreshTokenRemovedForAccountFromSource( + const CoreAccountId& account_id, + const std::string& source) {} + }; + + void AddDiagnosticsObserver(DiagnosticsObserver* observer); + void RemoveDiagnosticsObserver(DiagnosticsObserver* observer); + + // ************************************************************************** + // NOTE: All public methods methods below are either intended to be used only + // by signin code, or are slated for deletion. Most IdentityManager consumers + // should not need to interact with any methods below this line. + // ************************************************************************** + + IdentityManager( + std::unique_ptr<AccountTrackerService> account_tracker_service, + std::unique_ptr<ProfileOAuth2TokenService> token_service, + std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, + std::unique_ptr<PrimaryAccountManager> primary_account_manager, + std::unique_ptr<AccountFetcherService> account_fetcher_service, + std::unique_ptr<PrimaryAccountMutator> primary_account_mutator, + std::unique_ptr<AccountsMutator> accounts_mutator, + std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator, + std::unique_ptr<DiagnosticsProvider> diagnostics_provider, + std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer); + ~IdentityManager() override; + + // Performs initialization that is dependent on the network being + // initialized. + void OnNetworkInitialized(); + + // Returns |true| if migration of the account ID from normalized email is + // supported for the current platform. + static bool IsAccountIdMigrationSupported(); + + // Registers per-install prefs used by this class. + static void RegisterLocalStatePrefs(PrefRegistrySimple* registry); + + // Registers per-profile prefs used by this class. + static void RegisterProfilePrefs(PrefRegistrySimple* registry); + + // Picks the correct account_id for the specified account depending on the + // migration state. + // TODO(https://crbug.com/883272): Remove once all platform have migrated to + // the new account_id based on gaia (currently, only Chrome OS remains). + CoreAccountId PickAccountIdForAccount(const std::string& gaia, + const std::string& email) const; + + // Possible values for the account ID migration state, needs to be kept in + // sync with AccountTrackerService::AccountIdMigrationState. + enum AccountIdMigrationState { + MIGRATION_NOT_STARTED = 0, + MIGRATION_IN_PROGRESS = 1, + MIGRATION_DONE = 2, + NUM_MIGRATION_STATES + }; + + // Returns the currently saved state for the migration of accounts IDs. + AccountIdMigrationState GetAccountIdMigrationState() const; + +#if !defined(OS_IOS) && !defined(OS_ANDROID) + // Explicitly triggers the loading of accounts in the context of supervised + // users. + // TODO(https://crbug.com/860492): Remove this method when supervised users + // support is eliminated. + void DeprecatedLoadCredentialsForSupervisedUser( + const CoreAccountId& primary_account_id); +#endif + + // Returns pointer to the object used to obtain diagnostics about the internal + // state of IdentityManager. + DiagnosticsProvider* GetDiagnosticsProvider(); + +#if defined(OS_IOS) + // Forces the processing of GaiaCookieManagerService::OnCookieChange. On + // iOS, it's necessary to force-trigger the processing of cookie changes + // from the client as the normal mechanism for internally observing them + // is not wired up. + // TODO(https://crbug.com/930582) : Remove the need to expose this method + // or move it to the network::CookieManager. + void ForceTriggerOnCookieChange(); +#endif + +#if defined(OS_ANDROID) + // Reloads the accounts in the token service from the system accounts. This + // API calls ProfileOAuth2TokenServiceDelegate::ReloadAccountsFromSystem and + // it triggers platform specific implementation for Android. NOTE: In normal + // usage, this method SHOULD NOT be called. + // TODO(https://crbug.com/930094): Eliminate the need to expose this. + void LegacyReloadAccountsFromSystem(); + + // Returns a pointer to the AccountTrackerService Java instance associated + // with this object. + // TODO(https://crbug.com/934688): Eliminate this method once + // AccountTrackerService.java has no more client usage. + base::android::ScopedJavaLocalRef<jobject> + LegacyGetAccountTrackerServiceJavaObject(); + + // Returns a pointer to the OAuth2TokenService Java instance associated + // with this object. + // TODO(https://crbug.com/934688): Eliminate this method once + // OAuth2TokenService.java has no more client usage. + base::android::ScopedJavaLocalRef<jobject> + LegacyGetOAuth2TokenServiceJavaObject(); + + // This method has the contractual assumption that the account is a known + // account and has as its semantics that it fetches the account info for the + // account, triggering an OnExtendedAccountInfoUpdated() callback if the info + // was successfully fetched. + void ForceRefreshOfExtendedAccountInfo(const CoreAccountId& account_id); +#endif + + private: + // These test helpers need to use some of the private methods below. + friend CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, + const std::string& email); + friend void SetRefreshTokenForPrimaryAccount( + IdentityManager* identity_manager, + const std::string& token_value); + friend void SetInvalidRefreshTokenForPrimaryAccount( + IdentityManager* identity_manager); + friend void RemoveRefreshTokenForPrimaryAccount( + IdentityManager* identity_manager); + friend AccountInfo MakePrimaryAccountAvailable( + IdentityManager* identity_manager, + const std::string& email); + friend void ClearPrimaryAccount(IdentityManager* identity_manager, + ClearPrimaryAccountPolicy policy); + friend AccountInfo MakeAccountAvailable(IdentityManager* identity_manager, + const std::string& email); + friend AccountInfo MakeAccountAvailableWithCookies( + IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::string& email, + const std::string& gaia_id); + friend void SetRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id, + const std::string& token_value); + friend void SetInvalidRefreshTokenForAccount( + IdentityManager* identity_manager, + const std::string& account_id); + friend void RemoveRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id); + friend void UpdateAccountInfoForAccount(IdentityManager* identity_manager, + AccountInfo account_info); + friend void SetFreshnessOfAccountsInGaiaCookie( + IdentityManager* identity_manager, + bool accounts_are_fresh); + friend void UpdatePersistentErrorOfRefreshTokenForAccount( + IdentityManager* identity_manager, + const std::string& account_id, + const GoogleServiceAuthError& auth_error); + + friend void DisableAccessTokenFetchRetries(IdentityManager* identity_manager); + + friend void CancelAllOngoingGaiaCookieOperations( + IdentityManager* identity_manager); + + friend void SetCookieAccounts( + IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::vector<CookieParamsForTest>& cookie_accounts); + + friend void SimulateSuccessfulFetchOfAccountInfo( + IdentityManager* identity_manager, + const std::string& account_id, + const std::string& email, + const std::string& gaia, + const std::string& hosted_domain, + const std::string& full_name, + const std::string& given_name, + const std::string& locale, + const std::string& picture_url); + + // These friends are temporary during the conversion process. + // TODO(https://crbug.com/889902): Delete this when conversion is done. + friend SigninManagerAndroid; + + // Temporary access to getters (e.g. GetTokenService()). + // TODO(https://crbug.com/944127): Remove this friendship by + // extending identity_test_utils.h as needed. + friend IdentityTestEnvironment; + + // IdentityManagerTest reaches into IdentityManager internals in + // order to drive its behavior. + // TODO(https://crbug.com/943135): Find a better way to accomplish this. + friend IdentityManagerTest; + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + PrimaryAccountInfoAfterSigninAndAccountRemoval); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + PrimaryAccountInfoAfterSigninAndRefreshTokenRemoval); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, RemoveAccessTokenFromCache); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CreateAccessTokenFetcherWithCustomURLLoaderFactory); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, ObserveAccessTokenFetch); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + ObserveAccessTokenRequestCompletionWithRefreshToken); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + BatchChangeObserversAreNotifiedOnCredentialsUpdate); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, RemoveAccessTokenFromCache); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CreateAccessTokenFetcherWithCustomURLLoaderFactory); + FRIEND_TEST_ALL_PREFIXES( + IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithNoAccounts); + FRIEND_TEST_ALL_PREFIXES( + IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithOneAccount); + FRIEND_TEST_ALL_PREFIXES( + IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithTwoAccounts); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnUpdateToSignOutAccountsInCookie); + FRIEND_TEST_ALL_PREFIXES( + IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithStaleAccounts); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnSuccessfulAdditionOfAccountToCookie); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnFailureAdditionOfAccountToCookie); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnSetAccountsInCookieCompleted_Success); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnSetAccountsInCookieCompleted_Failure); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + CallbackSentOnAccountsCookieDeletedByUserAction); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, OnNetworkInitialized); + FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, + ForceRefreshOfExtendedAccountInfo); + + // Private getters used for testing only (i.e. see identity_test_utils.h). + PrimaryAccountManager* GetPrimaryAccountManager(); + ProfileOAuth2TokenService* GetTokenService(); + AccountTrackerService* GetAccountTrackerService(); + AccountFetcherService* GetAccountFetcherService(); + GaiaCookieManagerService* GetGaiaCookieManagerService(); + + // Populates and returns an AccountInfo object corresponding to |account_id|, + // which must be an account with a refresh token. + AccountInfo GetAccountInfoForAccountWithRefreshToken( + const CoreAccountId& account_id) const; + + // Sets primary account to |account_info| and updates the unconsented primary + // account. + void SetPrimaryAccountInternal(base::Optional<CoreAccountInfo> account_info); + + // Updates the cached version of unconsented primary account and notifies the + // observers if there is any change. + void UpdateUnconsentedPrimaryAccount(); + + // Figures out and returns the current unconsented primary account based on + // current cookies. + base::Optional<CoreAccountInfo> ComputeUnconsentedPrimaryAccountInfo() const; + + // PrimaryAccountManager callbacks: + void GoogleSigninSucceeded(const AccountInfo& account_info); + void GoogleSignedOut(const AccountInfo& account_info); + void AuthenticatedAccountSet(const AccountInfo& account_info); + void AuthenticatedAccountCleared(); + + // OAuth2TokenServiceObserver: + void OnRefreshTokenAvailable(const CoreAccountId& account_id) override; + void OnRefreshTokenRevoked(const CoreAccountId& account_id) override; + void OnRefreshTokensLoaded() override; + void OnEndBatchChanges() override; + void OnAuthErrorChanged(const CoreAccountId& account_id, + const GoogleServiceAuthError& auth_error) override; + + // GaiaCookieManagerService callbacks: + void OnGaiaAccountsInCookieUpdated( + const std::vector<gaia::ListedAccount>& signed_in_accounts, + const std::vector<gaia::ListedAccount>& signed_out_accounts, + const GoogleServiceAuthError& error); + void OnGaiaCookieDeletedByUserAction(); + + // OAuth2AccessTokenManager::DiagnosticsObserver + void OnAccessTokenRequested(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::ScopeSet& scopes) override; + void OnFetchAccessTokenComplete(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::ScopeSet& scopes, + GoogleServiceAuthError error, + base::Time expiration_time) override; + void OnAccessTokenRemoved(const CoreAccountId& account_id, + const identity::ScopeSet& scopes) override; + + // ProfileOAuth2TokenService callbacks: + void OnRefreshTokenAvailableFromSource(const CoreAccountId& account_id, + bool is_refresh_token_valid, + const std::string& source); + void OnRefreshTokenRevokedFromSource(const CoreAccountId& account_id, + const std::string& source); + + // AccountTrackerService callbacks: + void OnAccountUpdated(const AccountInfo& info); + void OnAccountRemoved(const AccountInfo& info); + + // Backing signin classes. + std::unique_ptr<AccountTrackerService> account_tracker_service_; + std::unique_ptr<ProfileOAuth2TokenService> token_service_; + std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service_; + std::unique_ptr<PrimaryAccountManager> primary_account_manager_; + std::unique_ptr<AccountFetcherService> account_fetcher_service_; + + // PrimaryAccountMutator instance. May be null if mutation of the primary + // account state is not supported on the current platform. + std::unique_ptr<PrimaryAccountMutator> primary_account_mutator_; + + // AccountsMutator instance. May be null if mutation of accounts is not + // supported on the current platform. + std::unique_ptr<AccountsMutator> accounts_mutator_; + + // AccountsCookieMutator instance. Guaranteed to be non-null, as this + // functionality is supported on all platforms. + std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator_; + + // DiagnosticsProvider instance. + std::unique_ptr<DiagnosticsProvider> diagnostics_provider_; + + // DeviceAccountsSynchronizer instance. + std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer_; + + // Lists of observers. + // Makes sure lists are empty on destruction. + base::ObserverList<Observer, true>::Unchecked observer_list_; + base::ObserverList<DiagnosticsObserver, true>::Unchecked + diagnostics_observer_list_; + + // If |primary_account_| is set, it must equal |unconsented_primary_account_|. + base::Optional<CoreAccountInfo> primary_account_; + base::Optional<CoreAccountInfo> unconsented_primary_account_; + + DISALLOW_COPY_AND_ASSIGN(IdentityManager); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_MANAGER_H_ diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc new file mode 100644 index 00000000000..7dc60a28104 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc @@ -0,0 +1,173 @@ +// 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/public/identity_manager/identity_manager_builder.h" + +#include <string> +#include <utility> + +#include "components/image_fetcher/core/image_decoder.h" +#include "components/prefs/pref_service.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h" +#include "components/signin/internal/identity_manager/diagnostics_provider_impl.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/primary_account_mutator_impl.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/signin_client.h" +#include "components/signin/public/identity_manager/accounts_mutator.h" +#include "components/signin/public/identity_manager/device_accounts_synchronizer.h" +#include "components/signin/public/identity_manager/identity_manager.h" + +#if !defined(OS_ANDROID) +#include "components/signin/public/webdata/token_web_data.h" +#endif + +#if defined(OS_IOS) +#include "components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h" +#include "components/signin/public/identity_manager/ios/device_accounts_provider.h" +#endif + +#if !defined(OS_ANDROID) && !defined(OS_IOS) +#include "components/signin/internal/identity_manager/accounts_mutator_impl.h" +#endif + +#if !defined(OS_CHROMEOS) +#include "components/signin/internal/identity_manager/primary_account_policy_manager_impl.h" +#endif + +namespace signin { + +namespace { + +std::unique_ptr<AccountTrackerService> BuildAccountTrackerService( + PrefService* pref_service, + base::FilePath profile_path) { + auto account_tracker_service = std::make_unique<AccountTrackerService>(); + account_tracker_service->Initialize(pref_service, profile_path); + return account_tracker_service; +} + +std::unique_ptr<PrimaryAccountManager> BuildPrimaryAccountManager( + SigninClient* client, + AccountConsistencyMethod account_consistency, + AccountTrackerService* account_tracker_service, + ProfileOAuth2TokenService* token_service, + PrefService* local_state) { + std::unique_ptr<PrimaryAccountManager> primary_account_manager; + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager; +#if !defined(OS_CHROMEOS) + policy_manager = std::make_unique<PrimaryAccountPolicyManagerImpl>(client); +#endif + primary_account_manager = std::make_unique<PrimaryAccountManager>( + client, token_service, account_tracker_service, account_consistency, + std::move(policy_manager)); + primary_account_manager->Initialize(local_state); + return primary_account_manager; +} + +std::unique_ptr<AccountsMutator> BuildAccountsMutator( + PrefService* prefs, + AccountTrackerService* account_tracker_service, + ProfileOAuth2TokenService* token_service, + PrimaryAccountManager* primary_account_manager) { +#if !defined(OS_ANDROID) && !defined(OS_IOS) + return std::make_unique<AccountsMutatorImpl>( + token_service, account_tracker_service, primary_account_manager, prefs); +#else + return nullptr; +#endif +} + +std::unique_ptr<AccountFetcherService> BuildAccountFetcherService( + SigninClient* signin_client, + ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + std::unique_ptr<image_fetcher::ImageDecoder> image_decoder) { + auto account_fetcher_service = std::make_unique<AccountFetcherService>(); + account_fetcher_service->Initialize(signin_client, token_service, + account_tracker_service, + std::move(image_decoder)); + return account_fetcher_service; +} + +} // anonymous namespace + +IdentityManagerBuildParams::IdentityManagerBuildParams() = default; + +IdentityManagerBuildParams::~IdentityManagerBuildParams() = default; + +std::unique_ptr<IdentityManager> BuildIdentityManager( + IdentityManagerBuildParams* params) { + std::unique_ptr<AccountTrackerService> account_tracker_service = + BuildAccountTrackerService(params->pref_service, params->profile_path); + + std::unique_ptr<ProfileOAuth2TokenService> token_service = + BuildProfileOAuth2TokenService( + params->pref_service, account_tracker_service.get(), + params->network_connection_tracker, params->account_consistency, +#if defined(OS_CHROMEOS) + params->account_manager, params->is_regular_profile, +#endif +#if !defined(OS_ANDROID) + params->delete_signin_cookies_on_exit, params->token_web_data, +#endif +#if defined(OS_IOS) + std::move(params->device_accounts_provider), +#endif +#if defined(OS_WIN) + params->reauth_callback, +#endif + params->signin_client); + + auto gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>( + token_service.get(), params->signin_client); + + std::unique_ptr<PrimaryAccountManager> primary_account_manager = + BuildPrimaryAccountManager(params->signin_client, + params->account_consistency, + account_tracker_service.get(), + token_service.get(), params->local_state); + + auto primary_account_mutator = std::make_unique<PrimaryAccountMutatorImpl>( + account_tracker_service.get(), primary_account_manager.get(), + params->pref_service); + + std::unique_ptr<AccountsMutator> accounts_mutator = + BuildAccountsMutator(params->pref_service, account_tracker_service.get(), + token_service.get(), primary_account_manager.get()); + + auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>( + gaia_cookie_manager_service.get(), account_tracker_service.get()); + + auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>( + token_service.get(), gaia_cookie_manager_service.get()); + + std::unique_ptr<AccountFetcherService> account_fetcher_service = + BuildAccountFetcherService(params->signin_client, token_service.get(), + account_tracker_service.get(), + std::move(params->image_decoder)); + + std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer; +#if defined(OS_IOS) + device_accounts_synchronizer = + std::make_unique<DeviceAccountsSynchronizerImpl>( + token_service->GetDelegate()); +#endif + + return std::make_unique<IdentityManager>( + std::move(account_tracker_service), std::move(token_service), + std::move(gaia_cookie_manager_service), + std::move(primary_account_manager), std::move(account_fetcher_service), + std::move(primary_account_mutator), std::move(accounts_mutator), + std::move(accounts_cookie_mutator), std::move(diagnostics_provider), + std::move(device_accounts_synchronizer)); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.h b/chromium/components/signin/public/identity_manager/identity_manager_builder.h new file mode 100644 index 00000000000..e9c6300f3f6 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.h @@ -0,0 +1,92 @@ +// 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_PUBLIC_IDENTITY_MANAGER_IDENTITY_MANAGER_BUILDER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_MANAGER_BUILDER_H_ + +#include <memory> + +#include "base/files/file_path.h" +#include "build/build_config.h" + +#if !defined(OS_ANDROID) +#include "base/memory/scoped_refptr.h" +#endif + +#if defined(OS_WIN) +#include "base/callback.h" +#endif + +class AccountTrackerService; +class PrefService; +class ProfileOAuth2TokenService; +class SigninClient; + +#if !defined(OS_ANDROID) +class TokenWebData; +#endif + +#if defined(OS_IOS) +class DeviceAccountsProvider; +#endif + +namespace image_fetcher { +class ImageDecoder; +} + +namespace network { +class NetworkConnectionTracker; +} + +#if defined(OS_CHROMEOS) +namespace chromeos { +class AccountManager; +} +#endif + +namespace signin { +enum class AccountConsistencyMethod; +class IdentityManager; + +struct IdentityManagerBuildParams { + IdentityManagerBuildParams(); + ~IdentityManagerBuildParams(); + + AccountConsistencyMethod account_consistency; + std::unique_ptr<AccountTrackerService> account_tracker_service; + std::unique_ptr<image_fetcher::ImageDecoder> image_decoder; + PrefService* local_state; + network::NetworkConnectionTracker* network_connection_tracker; + PrefService* pref_service; + base::FilePath profile_path; + SigninClient* signin_client; + std::unique_ptr<ProfileOAuth2TokenService> token_service; + +#if !defined(OS_ANDROID) + bool delete_signin_cookies_on_exit; + scoped_refptr<TokenWebData> token_web_data; +#endif + +#if defined(OS_CHROMEOS) + chromeos::AccountManager* account_manager; + bool is_regular_profile; +#endif + +#if defined(OS_IOS) + std::unique_ptr<DeviceAccountsProvider> device_accounts_provider; +#endif + +#if defined(OS_WIN) + base::RepeatingCallback<bool()> reauth_callback; +#endif +}; + +// Builds an IdentityManager instance from the supplied embedder-level +// dependencies. +std::unique_ptr<IdentityManager> BuildIdentityManager( + IdentityManagerBuildParams* params); + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_MANAGER_BUILDER_H_ diff --git a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc new file mode 100644 index 00000000000..6b44b400350 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc @@ -0,0 +1,2290 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_manager.h" + +#include <set> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/containers/flat_set.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/test/bind_test_util.h" +#include "base/test/scoped_task_environment.h" +#include "build/build_config.h" +#include "components/image_fetcher/core/fake_image_decoder.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h" +#include "components/signin/internal/identity_manager/diagnostics_provider_impl.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/primary_account_policy_manager_impl.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "components/signin/public/base/list_accounts_test_utils.h" +#include "components/signin/public/base/signin_switches.h" +#include "components/signin/public/base/test_signin_client.h" +#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/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/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" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.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" + +#if defined(OS_ANDROID) +#include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h" +#endif + +namespace signin { +namespace { + +const char kTestConsumerId[] = "dummy_consumer"; +const char kTestConsumerId2[] = "dummy_consumer 2"; +const char kTestGaiaId[] = "dummyId"; +const char kTestGaiaId2[] = "dummyId2"; +const char kTestGaiaId3[] = "dummyId3"; +const char kTestEmail[] = "me@gmail.com"; +const char kTestEmail2[] = "me2@gmail.com"; +const char kTestEmail3[] = "me3@gmail.com"; + +const char kTestHostedDomain[] = "example.com"; +const char kTestFullName[] = "full_name"; +const char kTestGivenName[] = "given_name"; +const char kTestLocale[] = "locale"; +const char kTestPictureUrl[] = "http://picture.example.com/picture.jpg"; + +#if defined(OS_CHROMEOS) +const char kTestEmailWithPeriod[] = "m.e@gmail.com"; +#endif + +// Subclass of FakeOAuth2AccessTokenManager with bespoke behavior. +class CustomFakeOAuth2AccessTokenManager : public FakeOAuth2AccessTokenManager { + public: + CustomFakeOAuth2AccessTokenManager( + OAuth2AccessTokenManager::Delegate* delegate) + : FakeOAuth2AccessTokenManager(delegate) {} + + void set_on_access_token_invalidated_info( + CoreAccountId expected_account_id_to_invalidate, + std::set<std::string> expected_scopes_to_invalidate, + std::string expected_access_token_to_invalidate, + base::OnceClosure callback) { + expected_account_id_to_invalidate_ = expected_account_id_to_invalidate; + expected_scopes_to_invalidate_ = expected_scopes_to_invalidate; + expected_access_token_to_invalidate_ = expected_access_token_to_invalidate; + on_access_token_invalidated_callback_ = std::move(callback); + } + + private: + friend class CustomFakeProfileOAuth2TokenService; + // OAuth2AccessTokenManager: + void InvalidateAccessTokenImpl(const CoreAccountId& account_id, + const std::string& client_id, + const identity::ScopeSet& scopes, + const std::string& access_token) override { + if (on_access_token_invalidated_callback_) { + EXPECT_EQ(expected_account_id_to_invalidate_, account_id); + EXPECT_EQ(expected_scopes_to_invalidate_, scopes); + EXPECT_EQ(expected_access_token_to_invalidate_, access_token); + + // It should trigger OnAccessTokenRemovedFromCache from + // IdentityManager::DiagnosticsObserver. + for (auto& observer : GetDiagnosticsObserversForTesting()) + observer.OnAccessTokenRemoved(account_id, scopes); + + std::move(on_access_token_invalidated_callback_).Run(); + } + } + + CoreAccountId expected_account_id_to_invalidate_; + std::set<std::string> expected_scopes_to_invalidate_; + std::string expected_access_token_to_invalidate_; + base::OnceClosure on_access_token_invalidated_callback_; +}; + +// Subclass of FakeProfileOAuth2TokenService with bespoke behavior. +class CustomFakeProfileOAuth2TokenService + : public FakeProfileOAuth2TokenService { + public: + CustomFakeProfileOAuth2TokenService(PrefService* user_prefs) + : FakeProfileOAuth2TokenService(user_prefs) { + OverrideAccessTokenManagerForTesting( + std::make_unique<CustomFakeOAuth2AccessTokenManager>( + this /* OAuth2AccessTokenManager::Delegate* */)); + } + + void set_on_access_token_invalidated_info( + CoreAccountId expected_account_id_to_invalidate, + std::set<std::string> expected_scopes_to_invalidate, + std::string expected_access_token_to_invalidate, + base::OnceClosure callback) { + GetCustomAccessTokenManager()->set_on_access_token_invalidated_info( + expected_account_id_to_invalidate, expected_scopes_to_invalidate, + expected_access_token_to_invalidate, std::move(callback)); + } + + private: + CustomFakeOAuth2AccessTokenManager* GetCustomAccessTokenManager() { + return static_cast<CustomFakeOAuth2AccessTokenManager*>( + GetAccessTokenManager()); + } +}; + +class TestIdentityManagerDiagnosticsObserver + : IdentityManager::DiagnosticsObserver { + public: + explicit TestIdentityManagerDiagnosticsObserver( + IdentityManager* identity_manager) + : identity_manager_(identity_manager) { + identity_manager_->AddDiagnosticsObserver(this); + } + ~TestIdentityManagerDiagnosticsObserver() override { + identity_manager_->RemoveDiagnosticsObserver(this); + } + + void set_on_access_token_requested_callback(base::OnceClosure callback) { + on_access_token_requested_callback_ = std::move(callback); + } + + void set_on_access_token_request_completed_callback( + base::OnceClosure callback) { + on_access_token_request_completed_callback_ = std::move(callback); + } + + const std::string& token_requestor_account_id() { + return token_requestor_account_id_; + } + const std::string& token_requestor_consumer_id() { + return token_requestor_consumer_id_; + } + const identity::ScopeSet& token_requestor_scopes() { + return token_requestor_scopes_; + } + const std::string& token_remover_account_id() { + return token_remover_account_id_; + } + const identity::ScopeSet& token_remover_scopes() { + return token_remover_scopes_; + } + const std::string& 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() { + return access_token_request_completed_scopes_; + } + const GoogleServiceAuthError& on_access_token_request_completed_error() { + return access_token_request_completed_error_; + } + + private: + // IdentityManager::DiagnosticsObserver: + void OnAccessTokenRequested(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::ScopeSet& scopes) override { + token_requestor_account_id_ = account_id; + token_requestor_consumer_id_ = consumer_id; + token_requestor_scopes_ = scopes; + + if (on_access_token_requested_callback_) + std::move(on_access_token_requested_callback_).Run(); + } + + void OnAccessTokenRemovedFromCache( + const CoreAccountId& account_id, + const identity::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, + GoogleServiceAuthError error, + base::Time expiration_time) override { + access_token_request_completed_account_id_ = account_id; + access_token_request_completed_consumer_id_ = consumer_id; + access_token_request_completed_scopes_ = scopes; + access_token_request_completed_error_ = error; + + if (on_access_token_request_completed_callback_) + std::move(on_access_token_request_completed_callback_).Run(); + } + + IdentityManager* identity_manager_; + base::OnceClosure on_access_token_requested_callback_; + base::OnceClosure on_access_token_request_completed_callback_; + std::string token_requestor_account_id_; + std::string token_requestor_consumer_id_; + std::string token_remover_account_id_; + identity::ScopeSet token_requestor_scopes_; + identity::ScopeSet token_remover_scopes_; + std::string access_token_request_completed_account_id_; + std::string access_token_request_completed_consumer_id_; + identity::ScopeSet access_token_request_completed_scopes_; + GoogleServiceAuthError access_token_request_completed_error_; +}; + +} // namespace + +class IdentityManagerTest : public testing::Test { + protected: + IdentityManagerTest() + : signin_client_(&pref_service_, &test_url_loader_factory_) { + IdentityManager::RegisterProfilePrefs(pref_service_.registry()); + IdentityManager::RegisterLocalStatePrefs(pref_service_.registry()); + + RecreateIdentityManager( + AccountConsistencyMethod::kDisabled, + PrimaryAccountManagerSetup::kWithAuthenticatedAccout); + } + + ~IdentityManagerTest() override { signin_client_.Shutdown(); } + + void SetUp() override { + primary_account_id_ = + identity_manager_->PickAccountIdForAccount(kTestGaiaId, kTestEmail); + } + + IdentityManager* identity_manager() { return identity_manager_.get(); } + + TestIdentityManagerObserver* identity_manager_observer() { + return identity_manager_observer_.get(); + } + + TestIdentityManagerDiagnosticsObserver* + identity_manager_diagnostics_observer() { + return identity_manager_diagnostics_observer_.get(); + } + + AccountTrackerService* account_tracker() { + return identity_manager()->GetAccountTrackerService(); + } + + CustomFakeProfileOAuth2TokenService* token_service() { + return static_cast<CustomFakeProfileOAuth2TokenService*>( + identity_manager()->GetTokenService()); + } + + // See RecreateIdentityManager. + enum class PrimaryAccountManagerSetup { + kWithAuthenticatedAccout, + kNoAuthenticatedAccount + }; + + // Used by some tests that need to re-instantiate IdentityManager after + // performing some other setup. + void RecreateIdentityManager() { + RecreateIdentityManager( + AccountConsistencyMethod::kDisabled, + PrimaryAccountManagerSetup::kNoAuthenticatedAccount); + } + + // Recreates IdentityManager with given |account_consistency| and optionally + // seeds with an authenticated account depending on + // |primary_account_manager_setup|. This process destroys any existing + // IdentityManager and its dependencies, then remakes them. Dependencies that + // outlive PrimaryAccountManager (e.g. SigninClient) will be reused. + void RecreateIdentityManager( + AccountConsistencyMethod account_consistency, + PrimaryAccountManagerSetup primary_account_manager_setup) { + // Remove observers first, otherwise IdentityManager destruction might + // trigger a DCHECK because there are still living observers. + identity_manager_observer_.reset(); + identity_manager_diagnostics_observer_.reset(); + identity_manager_.reset(); + + auto token_service = + std::make_unique<CustomFakeProfileOAuth2TokenService>(&pref_service_); + + auto gaia_cookie_manager_service = + std::make_unique<GaiaCookieManagerService>(token_service.get(), + &signin_client_); + + auto account_tracker_service = std::make_unique<AccountTrackerService>(); + account_tracker_service->Initialize(&pref_service_, base::FilePath()); + + auto account_fetcher_service = std::make_unique<AccountFetcherService>(); + account_fetcher_service->Initialize( + &signin_client_, token_service.get(), account_tracker_service.get(), + std::make_unique<image_fetcher::FakeImageDecoder>()); + + DCHECK_EQ(account_consistency, AccountConsistencyMethod::kDisabled) + << "AccountConsistency is not used by PrimaryAccountManager"; + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager; +#if !defined(OS_CHROMEOS) + policy_manager = + std::make_unique<PrimaryAccountPolicyManagerImpl>(&signin_client_); +#endif + auto primary_account_manager = std::make_unique<PrimaryAccountManager>( + &signin_client_, token_service.get(), account_tracker_service.get(), + account_consistency, std::move(policy_manager)); + + // Passing this switch ensures that the new PrimaryAccountManager starts + // with a clean slate. Otherwise PrimaryAccountManager::Initialize will use + // the account id stored in prefs::kGoogleServicesAccountId. + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + cmd_line->AppendSwitch(switches::kClearTokenService); + + primary_account_manager->Initialize(&pref_service_); + + if (primary_account_manager_setup == + PrimaryAccountManagerSetup::kWithAuthenticatedAccout) { + primary_account_manager->SetAuthenticatedAccountInfo(kTestGaiaId, + kTestEmail); + } + + auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>( + gaia_cookie_manager_service.get(), account_tracker_service.get()); + + auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>( + token_service.get(), gaia_cookie_manager_service.get()); + + identity_manager_.reset(new IdentityManager( + std::move(account_tracker_service), std::move(token_service), + std::move(gaia_cookie_manager_service), + std::move(primary_account_manager), std::move(account_fetcher_service), + nullptr, nullptr, std::move(accounts_cookie_mutator), + std::move(diagnostics_provider), nullptr)); + identity_manager_observer_.reset( + new TestIdentityManagerObserver(identity_manager_.get())); + identity_manager_diagnostics_observer_.reset( + new TestIdentityManagerDiagnosticsObserver(identity_manager_.get())); + } + + void SimulateAdditionOfAccountToCookieSuccess(GaiaAuthConsumer* consumer, + const std::string& data) { + consumer->OnMergeSessionSuccess(data); + } + + void SimulateAdditionOfAccountToCookieSuccessFailure( + GaiaAuthConsumer* consumer, + const GoogleServiceAuthError& error) { + consumer->OnMergeSessionFailure(error); + } + + void SimulateCookieDeletedByUser( + network::mojom::CookieChangeListener* listener, + const net::CanonicalCookie& cookie) { + listener->OnCookieChange(cookie, + network::mojom::CookieChangeCause::EXPLICIT); + } + + void SimulateOAuthMultiloginFinished(GaiaCookieManagerService* manager, + SetAccountsInCookieResult error) { + manager->OnSetAccountsFinished(error); + } + + const CoreAccountId& primary_account_id() const { + return primary_account_id_; + } + + TestSigninClient* signin_client() { return &signin_client_; } + + network::TestURLLoaderFactory* test_url_loader_factory() { + return &test_url_loader_factory_; + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + sync_preferences::TestingPrefServiceSyncable pref_service_; + network::TestURLLoaderFactory test_url_loader_factory_; + TestSigninClient signin_client_; + std::unique_ptr<IdentityManager> identity_manager_; + std::unique_ptr<TestIdentityManagerObserver> identity_manager_observer_; + std::unique_ptr<TestIdentityManagerDiagnosticsObserver> + identity_manager_diagnostics_observer_; + CoreAccountId primary_account_id_; + + DISALLOW_COPY_AND_ASSIGN(IdentityManagerTest); +}; + +// Test that IdentityManager starts off with the information in +// PrimaryAccountManager. +TEST_F(IdentityManagerTest, PrimaryAccountInfoAtStartup) { + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); + EXPECT_EQ(kTestEmail, primary_account_info.email); + + // Primary account is by definition also unconsented primary account. + EXPECT_EQ(primary_account_info, + identity_manager()->GetUnconsentedPrimaryAccountInfo()); + // There is no guarantee that this will be notified via callback on startup. +} + +// Signin/signout tests aren't relevant and cannot build on ChromeOS, which +// doesn't support signin/signout. +#if !defined(OS_CHROMEOS) +// Test that the user signing in results in firing of the IdentityManager +// observer callback and the IdentityManager's state being updated. +TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSignin) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + SetPrimaryAccount(identity_manager(), kTestEmail); + + CoreAccountInfo primary_account_from_set_callback = + identity_manager_observer()->PrimaryAccountFromSetCallback(); + EXPECT_EQ(kTestGaiaId, primary_account_from_set_callback.gaia); + EXPECT_EQ(kTestEmail, primary_account_from_set_callback.email); + + // Primary account is by definition also unconsented primary account. + EXPECT_EQ( + primary_account_from_set_callback, + identity_manager_observer()->UnconsentedPrimaryAccountFromCallback()); + + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); + EXPECT_EQ(kTestEmail, primary_account_info.email); + + EXPECT_EQ(primary_account_info, + identity_manager()->GetUnconsentedPrimaryAccountInfo()); + + CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); + EXPECT_EQ(primary_account_id, kTestGaiaId); + EXPECT_EQ(primary_account_id, primary_account_info.account_id); + + EXPECT_EQ(primary_account_id, + identity_manager()->GetUnconsentedPrimaryAccountId()); +} + +// Test that the user signing out results in firing of the IdentityManager +// observer callback and the IdentityManager's state being updated. +TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSigninAndSignout) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + // First ensure that the user is signed in from the POV of the + // IdentityManager. + SetPrimaryAccount(identity_manager(), kTestEmail); + + // Sign the user out and check that the IdentityManager responds + // appropriately. + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + CoreAccountInfo primary_account_from_cleared_callback = + identity_manager_observer()->PrimaryAccountFromClearedCallback(); + EXPECT_EQ(kTestGaiaId, primary_account_from_cleared_callback.gaia); + EXPECT_EQ(kTestEmail, primary_account_from_cleared_callback.email); + + // After the sign-out, there is no unconsented primary account. + EXPECT_TRUE(identity_manager_observer() + ->UnconsentedPrimaryAccountFromCallback() + .IsEmpty()); + + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + EXPECT_EQ("", primary_account_info.gaia); + EXPECT_EQ("", primary_account_info.email); + EXPECT_EQ(primary_account_info, + identity_manager()->GetUnconsentedPrimaryAccountInfo()); + + CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); + EXPECT_EQ("", primary_account_id); + EXPECT_EQ(primary_account_id, primary_account_info.account_id); + EXPECT_EQ(primary_account_id, + identity_manager()->GetUnconsentedPrimaryAccountId()); +} + +// Test that the primary account's core info remains tracked by the +// IdentityManager after signing in even after having removed the refresh token +// without signing out. +TEST_F(IdentityManagerTest, + PrimaryAccountInfoAfterSigninAndRefreshTokenRemoval) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + // First ensure that the user is signed in from the POV of the + // IdentityManager. + SetPrimaryAccount(identity_manager(), kTestEmail); + + identity_manager()->account_fetcher_service_->EnableAccountRemovalForTest(); + // Revoke the primary's account credentials from the token service and + // check that the returned CoreAccountInfo is still valid since the + // identity_manager stores it. + token_service()->RevokeCredentials(identity_manager()->GetPrimaryAccountId()); + + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); + EXPECT_EQ(kTestEmail, primary_account_info.email); + EXPECT_EQ(kTestGaiaId, primary_account_info.account_id); + EXPECT_EQ(primary_account_info, + identity_manager()->GetUnconsentedPrimaryAccountInfo()); + + CoreAccountId primary_account_id = identity_manager()->GetPrimaryAccountId(); + EXPECT_EQ(primary_account_id, kTestGaiaId); + EXPECT_EQ(primary_account_id, + identity_manager()->GetUnconsentedPrimaryAccountId()); +} +#endif // !defined(OS_CHROMEOS) + +TEST_F(IdentityManagerTest, HasPrimaryAccount) { + EXPECT_TRUE(identity_manager()->HasPrimaryAccount()); + EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + + // 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()); + +#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()); +#endif +} + +TEST_F(IdentityManagerTest, GetAccountsInteractionWithPrimaryAccount) { + // Should not have any refresh tokens at initialization. + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + + // Add a refresh token for the primary account and check that it shows up in + // GetAccountsWithRefreshTokens(). + SetRefreshTokenForPrimaryAccount(identity_manager()); + + std::vector<CoreAccountInfo> accounts_after_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_update.size()); + EXPECT_EQ(accounts_after_update[0].account_id, primary_account_id()); + EXPECT_EQ(accounts_after_update[0].gaia, kTestGaiaId); + EXPECT_EQ(accounts_after_update[0].email, kTestEmail); + + // Update the token and check that it doesn't change the state (or blow up). + SetRefreshTokenForPrimaryAccount(identity_manager()); + + std::vector<CoreAccountInfo> accounts_after_second_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_second_update.size()); + EXPECT_EQ(accounts_after_second_update[0].account_id, primary_account_id()); + EXPECT_EQ(accounts_after_second_update[0].gaia, kTestGaiaId); + EXPECT_EQ(accounts_after_second_update[0].email, kTestEmail); + + // Remove the token for the primary account and check that this is likewise + // reflected. + RemoveRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); +} + +TEST_F(IdentityManagerTest, + QueryingOfRefreshTokensInteractionWithPrimaryAccount) { + CoreAccountInfo account_info = identity_manager()->GetPrimaryAccountInfo(); + + // Should not have a refresh token for the primary account at initialization. + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Add a refresh token for the primary account and check that it affects this + // state. + SetRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Update the token and check that it doesn't change the state (or blow up). + SetRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Remove the token for the primary account and check that this is likewise + // reflected. + RemoveRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); +} + +TEST_F(IdentityManagerTest, QueryingOfRefreshTokensReflectsEmptyInitialState) { + CoreAccountInfo account_info = identity_manager()->GetPrimaryAccountInfo(); + CoreAccountId account_id = account_info.account_id; + + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + SetRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info.account_id)); + EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); +} + +TEST_F(IdentityManagerTest, GetAccountsInteractionWithSecondaryAccounts) { + // Should not have any refresh tokens at initialization. + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + + // Add a refresh token for a secondary account and check that it shows up in + // GetAccountsWithRefreshTokens(). + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id2); + + std::vector<CoreAccountInfo> accounts_after_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_update.size()); + EXPECT_EQ(accounts_after_update[0].account_id, account_id2); + EXPECT_EQ(accounts_after_update[0].gaia, kTestGaiaId2); + EXPECT_EQ(accounts_after_update[0].email, kTestEmail2); + + // Add a refresh token for a different secondary account and check that it + // also shows up in GetAccountsWithRefreshTokens(). + account_tracker()->SeedAccountInfo(kTestGaiaId3, kTestEmail3); + CoreAccountId account_id3 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId3).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id3); + + std::vector<CoreAccountInfo> accounts_after_second_update = + identity_manager()->GetAccountsWithRefreshTokens(); + EXPECT_EQ(2u, accounts_after_second_update.size()); + + for (CoreAccountInfo account_info : accounts_after_second_update) { + if (account_info.account_id == account_id2) { + EXPECT_EQ(account_info.gaia, kTestGaiaId2); + EXPECT_EQ(account_info.email, kTestEmail2); + } else { + EXPECT_EQ(account_info.gaia, kTestGaiaId3); + EXPECT_EQ(account_info.email, kTestEmail3); + } + } + + // Remove the token for account2 and check that account3 is still present. + RemoveRefreshTokenForAccount(identity_manager(), account_id2); + + std::vector<CoreAccountInfo> accounts_after_third_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_third_update.size()); + EXPECT_EQ(accounts_after_third_update[0].account_id, account_id3); + EXPECT_EQ(accounts_after_third_update[0].gaia, kTestGaiaId3); + EXPECT_EQ(accounts_after_third_update[0].email, kTestEmail3); +} + +TEST_F(IdentityManagerTest, + HasPrimaryAccountWithRefreshTokenInteractionWithSecondaryAccounts) { + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Adding a refresh token for a secondary account shouldn't change anything + // about the primary account + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Adding a refresh token for a different secondary account should not do so + // either. + account_tracker()->SeedAccountInfo(kTestGaiaId3, kTestEmail3); + CoreAccountId account_id3 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId3).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id3); + + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Removing the token for account2 should have no effect. + RemoveRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); +} + +TEST_F(IdentityManagerTest, + HasAccountWithRefreshTokenInteractionWithSecondaryAccounts) { + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + AccountInfo account_info2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2); + CoreAccountId account_id2 = account_info2.account_id; + + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + + // Add a refresh token for account_info2 and check that this is reflected by + // HasAccountWithRefreshToken(.account_id). + SetRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + + // Go through the same process for a different secondary account. + account_tracker()->SeedAccountInfo(kTestGaiaId3, kTestEmail3); + AccountInfo account_info3 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId3); + CoreAccountId account_id3 = account_info3.account_id; + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info3.account_id)); + + SetRefreshTokenForAccount(identity_manager(), account_id3); + + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info3.account_id)); + + // Remove the token for account2. + RemoveRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info3.account_id)); +} + +TEST_F(IdentityManagerTest, + GetAccountsInteractionBetweenPrimaryAndSecondaryAccounts) { + // Should not have any refresh tokens at initialization. + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + + // Add a refresh token for a secondary account and check that it shows up in + // GetAccountsWithRefreshTokens(). + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id2); + + std::vector<CoreAccountInfo> accounts_after_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_update.size()); + EXPECT_EQ(accounts_after_update[0].account_id, account_id2); + EXPECT_EQ(accounts_after_update[0].gaia, kTestGaiaId2); + EXPECT_EQ(accounts_after_update[0].email, kTestEmail2); + + 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, + kTestEmail); + + // Add a refresh token for the primary account and check that it + // also shows up in GetAccountsWithRefreshTokens(). + SetRefreshTokenForPrimaryAccount(identity_manager()); + + std::vector<CoreAccountInfo> accounts_after_second_update = + identity_manager()->GetAccountsWithRefreshTokens(); + EXPECT_EQ(2u, accounts_after_second_update.size()); + + for (const CoreAccountInfo& account_info : accounts_after_second_update) { + if (account_info.account_id == account_id2) { + EXPECT_EQ(account_info.gaia, kTestGaiaId2); + EXPECT_EQ(account_info.email, kTestEmail2); + } else { + EXPECT_EQ(account_info.gaia, kTestGaiaId); + EXPECT_EQ(account_info.email, kTestEmail); + } + } + + EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo().email, kTestEmail); + EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo().email, + kTestEmail); + + // Remove the token for the primary account and check that account2 is still + // present. + RemoveRefreshTokenForPrimaryAccount(identity_manager()); + + std::vector<CoreAccountInfo> accounts_after_third_update = + identity_manager()->GetAccountsWithRefreshTokens(); + + EXPECT_EQ(1u, accounts_after_third_update.size()); + EXPECT_EQ(accounts_after_update[0].account_id, account_id2); + EXPECT_EQ(accounts_after_update[0].gaia, kTestGaiaId2); + EXPECT_EQ(accounts_after_update[0].email, kTestEmail2); + + 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, + kTestEmail); +} + +TEST_F( + IdentityManagerTest, + HasPrimaryAccountWithRefreshTokenInteractionBetweenPrimaryAndSecondaryAccounts) { + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + + // Add a refresh token for a secondary account and check that it doesn't + // impact the above state. + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + SetRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken()); + EXPECT_TRUE(identity_manager()->HasUnconsentedPrimaryAccount()); + + // 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()); + + // 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()); + + // 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()); +} + +TEST_F( + IdentityManagerTest, + HasAccountWithRefreshTokenInteractionBetweenPrimaryAndSecondaryAccounts) { + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + CoreAccountId primary_account_id = primary_account_info.account_id; + + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + AccountInfo account_info2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2); + std::string account_id2 = account_info2.account_id; + + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + + // Add a refresh token for account_info2 and check that this is reflected by + // HasAccountWithRefreshToken(.account_id). + SetRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + + // Go through the same process for the primary account. + SetRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); + + // Remove the token for account2. + RemoveRefreshTokenForAccount(identity_manager(), account_id2); + + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshToken(account_info2.account_id)); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnUpdateToErrorStateOfRefreshTokenForAccount) { + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + CoreAccountId primary_account_id = primary_account_info.account_id; + SetRefreshTokenForPrimaryAccount(identity_manager()); + + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + AccountInfo account_info2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2); + std::string account_id2 = account_info2.account_id; + SetRefreshTokenForAccount(identity_manager(), account_id2); + + GoogleServiceAuthError user_not_signed_up_error = + GoogleServiceAuthError(GoogleServiceAuthError::State::USER_NOT_SIGNED_UP); + GoogleServiceAuthError invalid_gaia_credentials_error = + GoogleServiceAuthError( + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); + GoogleServiceAuthError transient_error = GoogleServiceAuthError( + GoogleServiceAuthError::State::SERVICE_UNAVAILABLE); + + // Set a persistent error for |account_id2| and check that it's reflected. + token_service()->UpdateAuthErrorForTesting(account_id2, + user_not_signed_up_error); + EXPECT_EQ(account_id2, + identity_manager_observer() + ->AccountFromErrorStateOfRefreshTokenUpdatedCallback() + .account_id); + EXPECT_EQ(user_not_signed_up_error, + identity_manager_observer() + ->ErrorFromErrorStateOfRefreshTokenUpdatedCallback()); + + // A transient error should not cause a callback. + token_service()->UpdateAuthErrorForTesting(primary_account_id, + transient_error); + EXPECT_EQ(account_id2, + identity_manager_observer() + ->AccountFromErrorStateOfRefreshTokenUpdatedCallback() + .account_id); + EXPECT_EQ(user_not_signed_up_error, + identity_manager_observer() + ->ErrorFromErrorStateOfRefreshTokenUpdatedCallback()); + + // Set a different persistent error for the primary account and check that + // it's reflected. + token_service()->UpdateAuthErrorForTesting(primary_account_id, + invalid_gaia_credentials_error); + EXPECT_EQ(primary_account_id, + identity_manager_observer() + ->AccountFromErrorStateOfRefreshTokenUpdatedCallback() + .account_id); + EXPECT_EQ(invalid_gaia_credentials_error, + identity_manager_observer() + ->ErrorFromErrorStateOfRefreshTokenUpdatedCallback()); +} + +TEST_F(IdentityManagerTest, GetErrorStateOfRefreshTokenForAccount) { + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + CoreAccountId primary_account_id = primary_account_info.account_id; + + // A primary account without a refresh token should not be in an error + // state, and setting a refresh token should not affect that. + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); + + SetRefreshTokenForPrimaryAccount(identity_manager()); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); + + // A secondary account without a refresh token should not be in an error + // state, and setting a refresh token should not affect that. + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + AccountInfo account_info2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2); + CoreAccountId account_id2 = account_info2.account_id; + EXPECT_EQ( + GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount(account_id2)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + + SetRefreshTokenForAccount(identity_manager(), account_id2); + EXPECT_EQ( + GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount(account_id2)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + + GoogleServiceAuthError user_not_signed_up_error = + GoogleServiceAuthError(GoogleServiceAuthError::State::USER_NOT_SIGNED_UP); + GoogleServiceAuthError invalid_gaia_credentials_error = + GoogleServiceAuthError( + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); + GoogleServiceAuthError transient_error = GoogleServiceAuthError( + GoogleServiceAuthError::State::SERVICE_UNAVAILABLE); + + // Set a persistent error for |account_id2| and check that it's reflected. + token_service()->UpdateAuthErrorForTesting(account_id2, + user_not_signed_up_error); + EXPECT_EQ( + user_not_signed_up_error, + identity_manager()->GetErrorStateOfRefreshTokenForAccount(account_id2)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); + + // A transient error should cause no change in the error state. + token_service()->UpdateAuthErrorForTesting(primary_account_id, + transient_error); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); + + // Set a different persistent error for the primary account and check that + // it's reflected. + token_service()->UpdateAuthErrorForTesting(primary_account_id, + invalid_gaia_credentials_error); + EXPECT_EQ( + user_not_signed_up_error, + identity_manager()->GetErrorStateOfRefreshTokenForAccount(account_id2)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + EXPECT_EQ(invalid_gaia_credentials_error, + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); + + // Remove the token for account2 and check that it goes back to having no + // error. + RemoveRefreshTokenForAccount(identity_manager(), account_id2); + EXPECT_EQ( + GoogleServiceAuthError::AuthErrorNone(), + identity_manager()->GetErrorStateOfRefreshTokenForAccount(account_id2)); + EXPECT_FALSE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + account_id2)); + EXPECT_EQ(invalid_gaia_credentials_error, + identity_manager()->GetErrorStateOfRefreshTokenForAccount( + primary_account_id)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id)); +} + +TEST_F(IdentityManagerTest, RemoveAccessTokenFromCache) { + std::set<std::string> scopes{"scope"}; + std::string access_token = "access_token"; + + identity_manager()->GetPrimaryAccountManager()->SetAuthenticatedAccountInfo( + kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(primary_account_id(), "refresh_token"); + + base::RunLoop run_loop; + token_service()->set_on_access_token_invalidated_info( + primary_account_id(), scopes, access_token, run_loop.QuitClosure()); + + identity_manager()->RemoveAccessTokenFromCache(primary_account_id(), scopes, + access_token); + + run_loop.Run(); + + // RemoveAccessTokenFromCache should lead to OnAccessTokenRemovedFromCache + // from IdentityManager::DiagnosticsObserver. + EXPECT_EQ( + primary_account_id(), + identity_manager_diagnostics_observer()->token_remover_account_id()); + EXPECT_EQ(scopes, + identity_manager_diagnostics_observer()->token_remover_scopes()); +} + +TEST_F(IdentityManagerTest, CreateAccessTokenFetcher) { + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + identity_manager()->GetPrimaryAccountId(), kTestConsumerId, scopes, + std::move(callback), AccessTokenFetcher::Mode::kImmediate); + EXPECT_TRUE(token_fetcher); +} + +TEST_F(IdentityManagerTest, + CreateAccessTokenFetcherWithCustomURLLoaderFactory) { + base::RunLoop run_loop; + identity_manager_diagnostics_observer() + ->set_on_access_token_requested_callback(run_loop.QuitClosure()); + + identity_manager()->GetPrimaryAccountManager()->SetAuthenticatedAccountInfo( + kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(primary_account_id(), "refresh_token"); + + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + + // We first create and AccessTokenFetcher with a custom URLLoaderFactory, + // to check that such factory is actually used in the requests generated. + network::TestURLLoaderFactory test_url_loader_factory; + scoped_refptr<network::SharedURLLoaderFactory> test_shared_url_loader_factory( + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &test_url_loader_factory)); + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + primary_account_id(), kTestConsumerId, test_shared_url_loader_factory, + scopes, std::move(callback), AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + // The URLLoaderFactory present in the pending request should match + // the one we specified when creating the AccessTokenFetcher. + std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests = + token_service()->GetPendingRequests(); + EXPECT_EQ(pending_requests.size(), 1U); + EXPECT_EQ(pending_requests[0].url_loader_factory, + test_shared_url_loader_factory); + + // The account ID and consumer's name should match the data passed as well. + EXPECT_EQ( + primary_account_id(), + identity_manager_diagnostics_observer()->token_requestor_account_id()); + EXPECT_EQ( + kTestConsumerId, + identity_manager_diagnostics_observer()->token_requestor_consumer_id()); + + // Cancel the pending request in preparation to check that creating an + // AccessTokenFetcher without a custom factory works as expected as well. + token_service()->IssueErrorForAllPendingRequestsForAccount( + primary_account_id(), + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + // Now add a second account and request an access token for it to test + // that the default URLLoaderFactory is used if none is specified. + base::RunLoop run_loop2; + identity_manager_diagnostics_observer() + ->set_on_access_token_requested_callback(run_loop2.QuitClosure()); + + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + token_service()->UpdateCredentials(account_id2, "refresh_token"); + + // No changes to the declared scopes and callback, we can reuse them. + std::unique_ptr<AccessTokenFetcher> token_fetcher2 = + identity_manager()->CreateAccessTokenFetcherForAccount( + account_id2, kTestConsumerId2, scopes, std::move(callback), + AccessTokenFetcher::Mode::kImmediate); + + run_loop2.Run(); + + // There should be one pending request now as well, just like before. + std::vector<FakeOAuth2AccessTokenManager::PendingRequest> pending_requests2 = + token_service()->GetPendingRequests(); + EXPECT_EQ(pending_requests2.size(), 1U); + + // The URLLoaderFactory present in the pending request should match + // the one created by default for the token service's delegate. + ProfileOAuth2TokenServiceDelegate* service_delegate = + token_service()->GetDelegate(); + EXPECT_EQ(pending_requests2[0].url_loader_factory, + service_delegate->GetURLLoaderFactory()); + + // The account ID and consumer's name should match the data passed again. + EXPECT_EQ( + account_id2, + identity_manager_diagnostics_observer()->token_requestor_account_id()); + EXPECT_EQ( + kTestConsumerId2, + identity_manager_diagnostics_observer()->token_requestor_consumer_id()); +} + +TEST_F(IdentityManagerTest, ObserveAccessTokenFetch) { + base::RunLoop run_loop; + identity_manager_diagnostics_observer() + ->set_on_access_token_requested_callback(run_loop.QuitClosure()); + + identity_manager()->GetPrimaryAccountManager()->SetAuthenticatedAccountInfo( + kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(primary_account_id(), "refresh_token"); + + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + identity_manager()->GetPrimaryAccountId(), kTestConsumerId, scopes, + std::move(callback), AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + EXPECT_EQ( + primary_account_id(), + identity_manager_diagnostics_observer()->token_requestor_account_id()); + EXPECT_EQ( + kTestConsumerId, + identity_manager_diagnostics_observer()->token_requestor_consumer_id()); + EXPECT_EQ(scopes, + identity_manager_diagnostics_observer()->token_requestor_scopes()); +} + +TEST_F(IdentityManagerTest, + ObserveAccessTokenRequestCompletionWithoutRefreshToken) { + base::RunLoop run_loop; + identity_manager_diagnostics_observer() + ->set_on_access_token_request_completed_callback(run_loop.QuitClosure()); + + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + // Account has no refresh token. + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + identity_manager()->GetPrimaryAccountId(), kTestConsumerId, scopes, + std::move(callback), AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + EXPECT_TRUE(token_fetcher); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP), + identity_manager_diagnostics_observer() + ->on_access_token_request_completed_error()); +} + +TEST_F(IdentityManagerTest, + ObserveAccessTokenRequestCompletionWithRefreshToken) { + base::RunLoop run_loop; + identity_manager_diagnostics_observer() + ->set_on_access_token_request_completed_callback(run_loop.QuitClosure()); + + identity_manager()->GetPrimaryAccountManager()->SetAuthenticatedAccountInfo( + kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(primary_account_id(), "refresh_token"); + token_service()->set_auto_post_fetch_response_on_message_loop(true); + + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + // This should result in a request for an access token without an error. + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + identity_manager()->GetPrimaryAccountId(), kTestConsumerId, scopes, + std::move(callback), AccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); + + EXPECT_TRUE(token_fetcher); + EXPECT_EQ(primary_account_id(), + identity_manager_diagnostics_observer() + ->on_access_token_request_completed_account_id()); + EXPECT_EQ(kTestConsumerId, + identity_manager_diagnostics_observer() + ->on_access_token_request_completed_consumer_id()); + EXPECT_EQ(scopes, identity_manager_diagnostics_observer() + ->on_access_token_request_completed_scopes()); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::NONE), + identity_manager_diagnostics_observer() + ->on_access_token_request_completed_error()); +} + +TEST_F(IdentityManagerTest, + ObserveAccessTokenRequestCompletionAfterRevokingRefreshToken) { + base::RunLoop run_loop; + identity_manager_diagnostics_observer() + ->set_on_access_token_request_completed_callback(run_loop.QuitClosure()); + + account_tracker()->SeedAccountInfo(kTestGaiaId2, kTestEmail2); + CoreAccountId account_id2 = + account_tracker()->FindAccountInfoByGaiaId(kTestGaiaId2).account_id; + token_service()->UpdateCredentials(account_id2, "refresh_token"); + + std::set<std::string> scopes{"scope"}; + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + // This should result in a request for an access token. + std::unique_ptr<AccessTokenFetcher> token_fetcher = + identity_manager()->CreateAccessTokenFetcherForAccount( + account_id2, kTestConsumerId2, scopes, std::move(callback), + AccessTokenFetcher::Mode::kImmediate); + + // Revoke the refresh token result cancelling access token request. + token_service()->RevokeCredentials(account_id2); + + run_loop.Run(); + + EXPECT_TRUE(token_fetcher); + EXPECT_EQ(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + identity_manager_diagnostics_observer() + ->on_access_token_request_completed_error()); +} + +TEST_F(IdentityManagerTest, GetAccountsCookieMutator) { + AccountsCookieMutator* mutator = + identity_manager()->GetAccountsCookieMutator(); + EXPECT_TRUE(mutator); +} + +#if !defined(OS_IOS) && !defined(OS_ANDROID) +// Tests that requesting a load of accounts results in the notification +// firing that tokens were loaded. +TEST_F(IdentityManagerTest, DeprecatedLoadCredentialsForSupervisedUser) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokensLoadedCallback( + run_loop.QuitClosure()); + + // Load the accounts and ensure that we see the resulting notification that + // they were loaded. + identity_manager()->DeprecatedLoadCredentialsForSupervisedUser(""); + run_loop.Run(); +} +#endif + +#if defined(OS_IOS) +TEST_F(IdentityManagerTest, ForceTriggerOnCookieChange) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseNoAccounts(test_url_loader_factory()); + // Forces the processing of OnCookieChange and it calls + // OnGaiaAccountsInCookieUpdated. + identity_manager()->ForceTriggerOnCookieChange(); + run_loop.Run(); +} +#endif + +#if defined(OS_CHROMEOS) +// On ChromeOS, AccountTrackerService first receives the normalized email +// address from GAIA and then later has it updated with the user's +// originally-specified version of their email address (at the time of that +// address' creation). This latter will differ if the user's originally- +// specified address was not in normalized form (e.g., if it contained +// periods). This test simulates such a flow in order to verify that +// IdentityManager correctly reflects the updated version. See crbug.com/842041 +// and crbug.com/842670 for further details. +TEST_F(IdentityManagerTest, IdentityManagerReflectsUpdatedEmailAddress) { + CoreAccountInfo primary_account_info = + identity_manager()->GetPrimaryAccountInfo(); + EXPECT_EQ(kTestGaiaId, primary_account_info.gaia); + EXPECT_EQ(kTestEmail, primary_account_info.email); + + // Simulate the flow wherein the user's email address was updated + // to the originally-created non-normalized version. + SimulateSuccessfulFetchOfAccountInfo( + identity_manager(), primary_account_info.account_id, kTestEmailWithPeriod, + kTestGaiaId, kTestHostedDomain, kTestFullName, kTestGivenName, + kTestLocale, kTestPictureUrl); + // Verify that IdentityManager reflects the update. + 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); +} +#endif + +TEST_F(IdentityManagerTest, + CallbackSentOnPrimaryAccountRefreshTokenUpdateWithValidToken) { + SetRefreshTokenForPrimaryAccount(identity_manager()); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(kTestGaiaId, account_info.gaia); + EXPECT_EQ(kTestEmail, account_info.email); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnPrimaryAccountRefreshTokenUpdateWithInvalidToken) { + SetInvalidRefreshTokenForPrimaryAccount(identity_manager()); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(kTestGaiaId, account_info.gaia); + EXPECT_EQ(kTestEmail, account_info.email); +} + +TEST_F(IdentityManagerTest, CallbackSentOnPrimaryAccountRefreshTokenRemoval) { + SetRefreshTokenForPrimaryAccount(identity_manager()); + + RemoveRefreshTokenForPrimaryAccount(identity_manager()); + + EXPECT_EQ( + primary_account_id(), + identity_manager_observer()->AccountIdFromRefreshTokenRemovedCallback()); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnSecondaryAccountRefreshTokenUpdateWithValidToken) { + AccountInfo expected_account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(expected_account_info.account_id, account_info.account_id); + EXPECT_EQ(expected_account_info.gaia, account_info.gaia); + EXPECT_EQ(expected_account_info.email, account_info.email); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnSecondaryAccountRefreshTokenUpdateWithInvalidToken) { + AccountInfo expected_account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + SetInvalidRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(expected_account_info.account_id, account_info.account_id); + EXPECT_EQ(expected_account_info.gaia, account_info.gaia); + EXPECT_EQ(expected_account_info.email, account_info.email); +} + +TEST_F(IdentityManagerTest, CallbackSentOnSecondaryAccountRefreshTokenRemoval) { + AccountInfo expected_account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + RemoveRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + EXPECT_EQ( + expected_account_info.account_id, + identity_manager_observer()->AccountIdFromRefreshTokenRemovedCallback()); +} + +#if !defined(OS_CHROMEOS) +TEST_F( + IdentityManagerTest, + CallbackSentOnSecondaryAccountRefreshTokenUpdateWithValidTokenWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(expected_account_info.account_id, account_info.account_id); + EXPECT_EQ(expected_account_info.gaia, account_info.gaia); + EXPECT_EQ(expected_account_info.email, account_info.email); +} + +TEST_F( + IdentityManagerTest, + CallbackSentOnSecondaryAccountRefreshTokenUpdateWithInvalidTokenWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + SetInvalidRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + CoreAccountInfo account_info = + identity_manager_observer()->AccountFromRefreshTokenUpdatedCallback(); + EXPECT_EQ(expected_account_info.account_id, account_info.account_id); + EXPECT_EQ(expected_account_info.gaia, account_info.gaia); + EXPECT_EQ(expected_account_info.email, account_info.email); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnSecondaryAccountRefreshTokenRemovalWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + RemoveRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + EXPECT_EQ( + expected_account_info.account_id, + identity_manager_observer()->AccountIdFromRefreshTokenRemovedCallback()); +} +#endif + +#if !defined(OS_CHROMEOS) && !defined(OS_IOS) && !defined(OS_ANDROID) +TEST_F( + IdentityManagerTest, + UnconsentedPrimaryAccountCallbackSentOnSecondaryAccountRefreshTokenUpdateWithValidTokenWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + 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_observer()->UnconsentedPrimaryAccountFromCallback(), + expected_account_info); +} + +TEST_F( + IdentityManagerTest, + UnconsentedPrimaryAccountCallbackSentOnSecondaryAccountRefreshTokenUpdateWithInvalidTokenWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + SetInvalidRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + // This is still an unconsented primary account, even with invalid refresh + // token. + EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), + expected_account_info); + EXPECT_EQ( + identity_manager_observer()->UnconsentedPrimaryAccountFromCallback(), + expected_account_info); +} + +TEST_F( + IdentityManagerTest, + UnconsentedPrimaryAccountCallbackSentOnSecondaryAccountRefreshTokenRemovalWhenNoPrimaryAccount) { + ClearPrimaryAccount(identity_manager(), ClearPrimaryAccountPolicy::DEFAULT); + + // Add an unconsented primary account, incl. proper cookies. + AccountInfo expected_account_info = MakeAccountAvailableWithCookies( + identity_manager(), test_url_loader_factory(), kTestEmail2, kTestGaiaId2); + EXPECT_EQ(kTestEmail2, expected_account_info.email); + + RemoveRefreshTokenForAccount(identity_manager(), + expected_account_info.account_id); + + // With no refresh token, there is no unconsented primary account any more. + CoreAccountInfo empty_info; + EXPECT_FALSE(identity_manager()->HasUnconsentedPrimaryAccount()); + EXPECT_EQ( + identity_manager_observer()->UnconsentedPrimaryAccountFromCallback(), + empty_info); + EXPECT_EQ(identity_manager()->GetUnconsentedPrimaryAccountInfo(), empty_info); +} +#endif + +TEST_F(IdentityManagerTest, CallbackSentOnRefreshTokenRemovalOfUnknownAccount) { + // When the token service is still loading credentials, it may send token + // revoked callbacks for accounts that it has never sent a token available + // callback. Our common test setup actually completes this loading, so use the + // *for_testing() method below to simulate the race condition and ensure that + // IdentityManager passes on the callback in this case. + token_service()->set_all_credentials_loaded_for_testing(false); + + CoreAccountId dummy_account_id("dummy_account"); + + base::RunLoop run_loop; + token_service()->RevokeCredentials(dummy_account_id); + run_loop.RunUntilIdle(); + + EXPECT_EQ( + dummy_account_id, + identity_manager_observer()->AccountIdFromRefreshTokenRemovedCallback()); +} + +TEST_F(IdentityManagerTest, IdentityManagerGetsTokensLoadedEvent) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokensLoadedCallback( + run_loop.QuitClosure()); + + // Credentials are already loaded in PrimaryAccountManager::Initialize() + // which runs even before the IdentityManager is created. That's why + // we fake the credentials loaded state and force another load in + // order to be able to capture the TokensLoaded event. + token_service()->set_all_credentials_loaded_for_testing(false); + token_service()->LoadCredentials(""); + run_loop.Run(); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithNoAccounts) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseNoAccounts(test_url_loader_factory()); + identity_manager()->GetGaiaCookieManagerService()->TriggerListAccounts(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar_info.signed_in_accounts.empty()); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithOneAccount) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseOneAccount(kTestEmail, kTestGaiaId, + test_url_loader_factory()); + identity_manager()->GetGaiaCookieManagerService()->TriggerListAccounts(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar_info.signed_in_accounts.size()); + ASSERT_TRUE(accounts_in_cookie_jar_info.signed_out_accounts.empty()); + + gaia::ListedAccount listed_account = + accounts_in_cookie_jar_info.signed_in_accounts[0]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account.id); + EXPECT_EQ(kTestGaiaId, listed_account.gaia_id); + EXPECT_EQ(kTestEmail, listed_account.email); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithTwoAccounts) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseTwoAccounts(kTestEmail, kTestGaiaId, kTestEmail2, + kTestGaiaId2, test_url_loader_factory()); + identity_manager()->GetGaiaCookieManagerService()->TriggerListAccounts(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh); + ASSERT_EQ(2u, accounts_in_cookie_jar_info.signed_in_accounts.size()); + ASSERT_TRUE(accounts_in_cookie_jar_info.signed_out_accounts.empty()); + + // Verify not only that both accounts are present but that they are listed in + // the expected order as well. + gaia::ListedAccount listed_account1 = + accounts_in_cookie_jar_info.signed_in_accounts[0]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); + EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); + EXPECT_EQ(kTestEmail, listed_account1.email); + + gaia::ListedAccount account_info2 = + accounts_in_cookie_jar_info.signed_in_accounts[1]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId2, kTestEmail2), + account_info2.id); + EXPECT_EQ(kTestGaiaId2, account_info2.gaia_id); + EXPECT_EQ(kTestEmail2, account_info2.email); +} + +TEST_F(IdentityManagerTest, CallbackSentOnUpdateToSignOutAccountsInCookie) { + struct SignedOutStatus { + int account_1; + int account_2; + } signed_out_status_set[] = {{0, 0}, {1, 0}, {0, 1}, {1, 1}}; + + for (const auto& signed_out_status : signed_out_status_set) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseWithParams( + {{kTestEmail, kTestGaiaId, true /* valid */, + signed_out_status.account_1 /* signed_out */, true /* verified */}, + {kTestEmail2, kTestGaiaId2, true /* valid */, + signed_out_status.account_2 /* signed_out */, true /* verified */}}, + test_url_loader_factory()); + + identity_manager()->GetGaiaCookieManagerService()->TriggerListAccounts(); + run_loop.Run(); + + unsigned int accounts_signed_out = + signed_out_status.account_1 + signed_out_status.account_2; + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_TRUE(accounts_in_cookie_jar_info.accounts_are_fresh); + ASSERT_EQ(2 - accounts_signed_out, + accounts_in_cookie_jar_info.signed_in_accounts.size()); + ASSERT_EQ(accounts_signed_out, + accounts_in_cookie_jar_info.signed_out_accounts.size()); + + // Verify not only that both accounts are present but that they are listed + // in the expected order as well. + // + // The two variables below, control the lookup indexes signed in and signed + // out accounts list, respectively. + int i = 0, j = 0; + gaia::ListedAccount listed_account1 = + signed_out_status.account_1 + ? accounts_in_cookie_jar_info.signed_out_accounts[i++] + : accounts_in_cookie_jar_info.signed_in_accounts[j++]; + if (!signed_out_status.account_1) + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); + EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); + EXPECT_EQ(kTestEmail, listed_account1.email); + + gaia::ListedAccount listed_account2 = + signed_out_status.account_2 + ? accounts_in_cookie_jar_info.signed_out_accounts[i++] + : accounts_in_cookie_jar_info.signed_in_accounts[j++]; + if (!signed_out_status.account_2) + EXPECT_EQ(identity_manager()->PickAccountIdForAccount(kTestGaiaId2, + kTestEmail2), + listed_account2.id); + EXPECT_EQ(kTestGaiaId2, listed_account2.gaia_id); + EXPECT_EQ(kTestEmail2, listed_account2.email); + } +} + +TEST_F(IdentityManagerTest, + CallbackSentOnUpdateToAccountsInCookieWithStaleAccounts) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + // Configure list accounts to return a permanent Gaia auth error. + SetListAccountsResponseWithUnexpectedServiceResponse( + test_url_loader_factory()); + identity_manager()->GetGaiaCookieManagerService()->TriggerListAccounts(); + run_loop.Run(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info = + identity_manager_observer() + ->AccountsInfoFromAccountsInCookieUpdatedCallback(); + EXPECT_FALSE(accounts_in_cookie_jar_info.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar_info.signed_in_accounts.empty()); + EXPECT_TRUE(accounts_in_cookie_jar_info.signed_out_accounts.empty()); +} + +TEST_F(IdentityManagerTest, GetAccountsInCookieJarWithNoAccounts) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseNoAccounts(test_url_loader_factory()); + + // Do an initial call to GetAccountsInCookieJar(). This call should return no + // accounts but should also trigger an internal update and eventual + // notification that the accounts in the cookie jar have been updated. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_FALSE(accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(accounts_in_cookie_jar.signed_out_accounts.empty()); + + run_loop.Run(); + + // The state of the accounts in IdentityManager should now reflect the + // internal update. + const AccountsInCookieJarInfo updated_accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + + EXPECT_TRUE(updated_accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(updated_accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(updated_accounts_in_cookie_jar.signed_out_accounts.empty()); +} + +TEST_F(IdentityManagerTest, GetAccountsInCookieJarWithOneAccount) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseOneAccount(kTestEmail, kTestGaiaId, + test_url_loader_factory()); + + // Do an initial call to GetAccountsInCookieJar(). This call should return no + // accounts but should also trigger an internal update and eventual + // notification that the accounts in the cookie jar have been updated. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_FALSE(accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(accounts_in_cookie_jar.signed_out_accounts.empty()); + + run_loop.Run(); + + // The state of the accounts in IdentityManager should now reflect the + // internal update. + const AccountsInCookieJarInfo& updated_accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + + EXPECT_TRUE(updated_accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(1u, updated_accounts_in_cookie_jar.signed_in_accounts.size()); + ASSERT_TRUE(updated_accounts_in_cookie_jar.signed_out_accounts.empty()); + + gaia::ListedAccount listed_account = + updated_accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account.id); + EXPECT_EQ(kTestGaiaId, listed_account.gaia_id); + EXPECT_EQ(kTestEmail, listed_account.email); +} + +TEST_F(IdentityManagerTest, GetAccountsInCookieJarWithTwoAccounts) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback( + run_loop.QuitClosure()); + + SetListAccountsResponseTwoAccounts(kTestEmail, kTestGaiaId, kTestEmail2, + kTestGaiaId2, test_url_loader_factory()); + + // Do an initial call to GetAccountsInCookieJar(). This call should return no + // accounts but should also trigger an internal update and eventual + // notification that the accounts in the cookie jar have been updated. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_FALSE(accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(accounts_in_cookie_jar.signed_out_accounts.empty()); + + run_loop.Run(); + + // The state of the accounts in IdentityManager should now reflect the + // internal update. + const AccountsInCookieJarInfo& updated_accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + + EXPECT_TRUE(updated_accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(2u, updated_accounts_in_cookie_jar.signed_in_accounts.size()); + ASSERT_TRUE(updated_accounts_in_cookie_jar.signed_out_accounts.empty()); + + // Verify not only that both accounts are present but that they are listed in + // the expected order as well. + gaia::ListedAccount listed_account1 = + updated_accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail), + listed_account1.id); + EXPECT_EQ(kTestGaiaId, listed_account1.gaia_id); + EXPECT_EQ(kTestEmail, listed_account1.email); + + gaia::ListedAccount listed_account2 = + updated_accounts_in_cookie_jar.signed_in_accounts[1]; + EXPECT_EQ( + identity_manager()->PickAccountIdForAccount(kTestGaiaId2, kTestEmail2), + listed_account2.id); + EXPECT_EQ(kTestGaiaId2, listed_account2.gaia_id); + EXPECT_EQ(kTestEmail2, listed_account2.email); +} + +TEST_F(IdentityManagerTest, CallbackSentOnSuccessfulAdditionOfAccountToCookie) { + const char kTestAccountId[] = "account_id"; + + std::string account_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + }); + + identity_manager()->GetGaiaCookieManagerService()->AddAccountToCookie( + kTestAccountId, gaia::GaiaSource::kChrome, + std::move(completion_callback)); + SimulateAdditionOfAccountToCookieSuccess( + identity_manager()->GetGaiaCookieManagerService(), "token"); + EXPECT_EQ(account_from_add_account_to_cookie_completed_callback, + kTestAccountId); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback, + GoogleServiceAuthError::AuthErrorNone()); +} + +TEST_F(IdentityManagerTest, CallbackSentOnFailureAdditionOfAccountToCookie) { + const char kTestAccountId[] = "account_id"; + + std::string account_from_add_account_to_cookie_completed_callback; + GoogleServiceAuthError error_from_add_account_to_cookie_completed_callback; + auto completion_callback = + base::BindLambdaForTesting([&](const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + account_from_add_account_to_cookie_completed_callback = account_id; + error_from_add_account_to_cookie_completed_callback = error; + }); + + identity_manager()->GetGaiaCookieManagerService()->AddAccountToCookie( + kTestAccountId, gaia::GaiaSource::kChrome, + std::move(completion_callback)); + + GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR); + SimulateAdditionOfAccountToCookieSuccessFailure( + identity_manager()->GetGaiaCookieManagerService(), error); + + EXPECT_EQ(account_from_add_account_to_cookie_completed_callback, + kTestAccountId); + EXPECT_EQ(error_from_add_account_to_cookie_completed_callback, error); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnSetAccountsInCookieCompleted_Success) { + const CoreAccountId kTestAccountId("account_id"); + const CoreAccountId kTestAccountId2("account_id2"); + const std::vector<std::pair<CoreAccountId, std::string>> accounts = { + {kTestAccountId, kTestAccountId.id}, + {kTestAccountId2, kTestAccountId2.id}}; + + SetAccountsInCookieResult + error_from_set_accounts_in_cookie_completed_callback; + auto completion_callback = base::BindLambdaForTesting( + [&error_from_set_accounts_in_cookie_completed_callback]( + SetAccountsInCookieResult error) { + error_from_set_accounts_in_cookie_completed_callback = error; + }); + + // Needed to insert request in the queue. + identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie( + accounts, gaia::GaiaSource::kChrome, std::move(completion_callback)); + + SimulateOAuthMultiloginFinished( + identity_manager()->GetGaiaCookieManagerService(), + SetAccountsInCookieResult::kSuccess); + + EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, + SetAccountsInCookieResult::kSuccess); +} + +TEST_F(IdentityManagerTest, + CallbackSentOnSetAccountsInCookieCompleted_Failure) { + const CoreAccountId kTestAccountId("account_id"); + const CoreAccountId kTestAccountId2("account_id2"); + const std::vector<std::pair<CoreAccountId, std::string>> accounts = { + {kTestAccountId, kTestAccountId.id}, + {kTestAccountId2, kTestAccountId2.id}}; + + SetAccountsInCookieResult + error_from_set_accounts_in_cookie_completed_callback; + auto completion_callback = base::BindLambdaForTesting( + [&error_from_set_accounts_in_cookie_completed_callback]( + SetAccountsInCookieResult error) { + error_from_set_accounts_in_cookie_completed_callback = error; + }); + + // Needed to insert request in the queue. + identity_manager()->GetGaiaCookieManagerService()->SetAccountsInCookie( + accounts, gaia::GaiaSource::kChrome, std::move(completion_callback)); + + // Sample an erroneous response. + SetAccountsInCookieResult error = SetAccountsInCookieResult::kPersistentError; + + SimulateOAuthMultiloginFinished( + identity_manager()->GetGaiaCookieManagerService(), error); + + EXPECT_EQ(error_from_set_accounts_in_cookie_completed_callback, error); +} + +TEST_F(IdentityManagerTest, CallbackSentOnAccountsCookieDeletedByUserAction) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnCookieDeletedByUserCallback( + run_loop.QuitClosure()); + net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/", + base::Time(), base::Time(), base::Time(), false, + false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT); + SimulateCookieDeletedByUser(identity_manager()->GetGaiaCookieManagerService(), + cookie); + run_loop.Run(); +} + +TEST_F(IdentityManagerTest, OnNetworkInitialized) { + auto test_cookie_manager = std::make_unique<network::TestCookieManager>(); + network::TestCookieManager* test_cookie_manager_ptr = + test_cookie_manager.get(); + signin_client()->set_cookie_manager(std::move(test_cookie_manager)); + + identity_manager()->OnNetworkInitialized(); + + base::RunLoop run_loop; + identity_manager_observer()->SetOnCookieDeletedByUserCallback( + run_loop.QuitClosure()); + + // Dispatch a known change of a known cookie instance *through the mojo + // pipe* in order to ensure the GCMS is listening to CookieManager changes. + // + // It is important the the cause of the change is known here (ie + // network::mojom::CookieChangeCause::EXPLICIT) so the test can block of the + // proper IdentityManager observer callback to be called (in this case + // OnAccountsCookieDeletedByUserAction). + // + // Note that this call differs from calling SimulateCookieDeletedByUser() + // directly in the sense that SimulateCookieDeletedByUser() does not go + // through any mojo pipe. + net::CanonicalCookie cookie("APISID", std::string(), ".google.com", "/", + base::Time(), base::Time(), base::Time(), false, + false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT); + test_cookie_manager_ptr->DispatchCookieChange( + cookie, network::mojom::CookieChangeCause::EXPLICIT); + run_loop.Run(); +} + +TEST_F(IdentityManagerTest, + BatchChangeObserversAreNotifiedOnCredentialsUpdate) { + identity_manager()->GetPrimaryAccountManager()->SetAuthenticatedAccountInfo( + kTestGaiaId, kTestEmail); + token_service()->UpdateCredentials(primary_account_id(), "refresh_token"); + + EXPECT_EQ(1ul, identity_manager_observer()->BatchChangeRecords().size()); + EXPECT_EQ(1ul, + identity_manager_observer()->BatchChangeRecords().at(0).size()); + EXPECT_EQ(primary_account_id(), + identity_manager_observer()->BatchChangeRecords().at(0).at(0)); +} + +// Checks that FindAccountInfoForAccountWithRefreshTokenByAccountId() returns +// information about the account if the account is found or nullopt if there +// are no accounts with requested |account_id|. +TEST_F(IdentityManagerTest, + FindAccountInfoForAccountWithRefreshTokenByAccountId) { + // Add an account (note: cannot use kTestEmail as it is already inserted + // by the fixture common code, so use a different address). + const AccountInfo foo_account_info = + MakeAccountAvailable(identity_manager(), "foo@bar.com"); + + base::Optional<AccountInfo> maybe_account_info; + maybe_account_info = + identity_manager()->FindAccountInfoForAccountWithRefreshTokenByAccountId( + "dummy_value"); + EXPECT_FALSE(maybe_account_info.has_value()); + + maybe_account_info = + identity_manager()->FindAccountInfoForAccountWithRefreshTokenByAccountId( + foo_account_info.account_id); + EXPECT_TRUE(maybe_account_info.has_value()); + EXPECT_EQ(foo_account_info.account_id, maybe_account_info.value().account_id); + EXPECT_EQ(foo_account_info.email, maybe_account_info.value().email); + EXPECT_EQ(foo_account_info.gaia, maybe_account_info.value().gaia); +} + +// Checks that FindAccountInfoForAccountWithRefreshTokenByEmailAddress() returns +// information about the account if the account is found or nullopt if there +// are no accounts with requested |email_address|. +TEST_F(IdentityManagerTest, + FindAccountInfoForAccountWithRefreshTokenByEmailAddress) { + // Add an account (note: cannot use kTestEmail as it is already inserted + // by the fixture common code, so use a different address). + const AccountInfo foo_account_info = + MakeAccountAvailable(identity_manager(), "foo@bar.com"); + + base::Optional<AccountInfo> maybe_account_info; + maybe_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress( + "dummy_value"); + EXPECT_FALSE(maybe_account_info.has_value()); + + maybe_account_info = + identity_manager() + ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress( + foo_account_info.email); + EXPECT_TRUE(maybe_account_info.has_value()); + EXPECT_EQ(foo_account_info.account_id, maybe_account_info.value().account_id); + EXPECT_EQ(foo_account_info.email, maybe_account_info.value().email); + EXPECT_EQ(foo_account_info.gaia, maybe_account_info.value().gaia); +} + +// Checks that FindAccountInfoForAccountWithRefreshTokenByGaiaId() returns +// information about the account if the account is found or nullopt if there +// are no accounts with requested |gaia_id|. +TEST_F(IdentityManagerTest, FindAccountInfoForAccountWithRefreshTokenByGaiaId) { + // Add an account (note: cannot use kTestEmail as it is already inserted + // by the fixture common code, so use a different address). + const AccountInfo foo_account_info = + MakeAccountAvailable(identity_manager(), "foo@bar.com"); + + base::Optional<AccountInfo> maybe_account_info; + maybe_account_info = + identity_manager()->FindAccountInfoForAccountWithRefreshTokenByGaiaId( + "dummy_value"); + EXPECT_FALSE(maybe_account_info.has_value()); + + maybe_account_info = + identity_manager()->FindAccountInfoForAccountWithRefreshTokenByGaiaId( + foo_account_info.gaia); + EXPECT_TRUE(maybe_account_info.has_value()); + EXPECT_EQ(foo_account_info.account_id, maybe_account_info.value().account_id); + EXPECT_EQ(foo_account_info.email, maybe_account_info.value().email); + EXPECT_EQ(foo_account_info.gaia, maybe_account_info.value().gaia); +} + +// Checks that AreRefreshTokensLoaded() returns true after LoadCredentials. +TEST_F(IdentityManagerTest, AreRefreshTokensLoaded) { + base::RunLoop run_loop; + identity_manager_observer()->SetOnRefreshTokensLoadedCallback( + run_loop.QuitClosure()); + + // Credentials are already loaded in PrimaryAccountManager::Initialize() + // which runs even before the IdentityManager is created. That's why + // we fake the credentials loaded state and force another load in + // order to test AreRefreshTokensLoaded. + token_service()->set_all_credentials_loaded_for_testing(false); + EXPECT_FALSE(identity_manager()->AreRefreshTokensLoaded()); + token_service()->LoadCredentials(""); + run_loop.Run(); + EXPECT_TRUE(identity_manager()->AreRefreshTokensLoaded()); +} + +TEST_F(IdentityManagerTest, AccountIdMigration_DoneOnInitialization) { + // Migration gets marked as DONE while initializing the AccountTrackerService + // on platforms supporting account ID migration only. + if (IdentityManager::IsAccountIdMigrationSupported()) { + EXPECT_EQ(identity_manager()->GetAccountIdMigrationState(), + IdentityManager::AccountIdMigrationState::MIGRATION_DONE); + } else { + EXPECT_EQ(identity_manager()->GetAccountIdMigrationState(), + IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED); + } +} + +// Checks that IdentityManager::Observer gets OnAccountUpdated when account info +// is updated. +TEST_F(IdentityManagerTest, ObserveOnAccountUpdated) { + const AccountInfo account_info = + MakeAccountAvailable(identity_manager(), kTestEmail3); + + SimulateSuccessfulFetchOfAccountInfo( + identity_manager(), account_info.account_id, account_info.email, + account_info.account_id, kTestHostedDomain, kTestFullName, kTestGivenName, + kTestLocale, kTestPictureUrl); + + EXPECT_EQ(account_info.account_id, identity_manager_observer() + ->AccountFromAccountUpdatedCallback() + .account_id); + EXPECT_EQ( + account_info.email, + identity_manager_observer()->AccountFromAccountUpdatedCallback().email); +} + +TEST_F(IdentityManagerTest, TestOnAccountRemovedWithInfoCallback) { + AccountInfo account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + EXPECT_EQ(kTestEmail2, account_info.email); + + account_tracker()->RemoveAccount(account_info.account_id); + + // Check if OnAccountRemovedWithInfo is called after removing |account_info| + // by RemoveAccount(). + EXPECT_TRUE( + identity_manager_observer()->WasCalledAccountRemovedWithInfoCallback()); + + // Check if the passed AccountInfo is the same to the removing one. + EXPECT_EQ(account_info.account_id, + identity_manager_observer() + ->AccountFromAccountRemovedWithInfoCallback() + .account_id); + EXPECT_EQ(account_info.email, + identity_manager_observer() + ->AccountFromAccountRemovedWithInfoCallback() + .email); +} + +TEST_F(IdentityManagerTest, TestPickAccountIdForAccount) { + const CoreAccountId account_id = + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail); + const bool account_id_migration_done = + identity_manager()->GetAccountIdMigrationState() == + IdentityManager::AccountIdMigrationState::MIGRATION_DONE; + if (account_id_migration_done) { + EXPECT_EQ(account_id, kTestGaiaId); + } else { + EXPECT_TRUE(gaia::AreEmailsSame(account_id, kTestEmail)); + } +} + +// Check that FindExtendedAccountInfoForAccount returns a valid account info +// iff the account is known, has refresh token and all the extended information +// is available. +TEST_F(IdentityManagerTest, FindExtendedAccountInfoForAccount) { + CoreAccountInfo account_info; + account_info.email = kTestEmail; + account_info.gaia = kTestGaiaId; + account_info.account_id = + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail); + + // FindExtendedAccountInfoForAccount() returns empty optional if the + // account_info is invalid. + EXPECT_FALSE(identity_manager() + ->FindExtendedAccountInfoForAccount(CoreAccountInfo{}) + .has_value()); + + // FindExtendedAccountInfoForAccount() returns empty optional if the + // account_info is unknown. + EXPECT_FALSE(identity_manager() + ->FindExtendedAccountInfoForAccount(account_info) + .has_value()); + + // Insert the core account information in the AccountTrackerService. + const CoreAccountId account_id = + account_tracker()->SeedAccountInfo(kTestGaiaId, kTestEmail); + ASSERT_EQ(account_info.account_id, account_id); + + // FindExtendedAccountInfoForAccount() returns empty optional if the account + // has no refresh token. + EXPECT_FALSE(identity_manager() + ->FindExtendedAccountInfoForAccount(account_info) + .has_value()); + + // Insert refresh token for account. + SetRefreshTokenForAccount(identity_manager(), account_info.account_id, + "refresh-token"); + + // FindExtendedAccountInfoForAccount() returns extended account information if + // the account is known and has valid refresh token. + const base::Optional<AccountInfo> extended_account_info = + identity_manager()->FindExtendedAccountInfoForAccount(account_info); + + ASSERT_TRUE(extended_account_info.has_value()); + EXPECT_EQ(account_info.gaia, extended_account_info.value().gaia); + EXPECT_EQ(account_info.email, extended_account_info.value().email); + EXPECT_EQ(account_info.account_id, extended_account_info.value().account_id); +} + +#if defined(OS_ANDROID) +TEST_F(IdentityManagerTest, ForceRefreshOfExtendedAccountInfo) { + // The flow of this test results in an interaction with + // ChildAccountInfoFetcherAndroid, which requires initialization in order to + // avoid a crash. + ChildAccountInfoFetcherAndroid::InitializeForTests(); + + identity_manager()->GetAccountFetcherService()->OnNetworkInitialized(); + AccountInfo account_info = + MakeAccountAvailable(identity_manager(), kTestEmail2); + identity_manager()->ForceRefreshOfExtendedAccountInfo( + account_info.account_id); + + SimulateSuccessfulFetchOfAccountInfo( + identity_manager(), account_info.account_id, account_info.email, + account_info.account_id, kTestHostedDomain, kTestFullName, kTestGivenName, + kTestLocale, kTestPictureUrl); + + const AccountInfo& refreshed_account_info = + identity_manager_observer()->AccountFromAccountUpdatedCallback(); + EXPECT_EQ(account_info.account_id, refreshed_account_info.account_id); + EXPECT_EQ(account_info.email, refreshed_account_info.email); + EXPECT_EQ(account_info.gaia, refreshed_account_info.gaia); + EXPECT_EQ(kTestHostedDomain, refreshed_account_info.hosted_domain); + EXPECT_EQ(kTestFullName, refreshed_account_info.full_name); + EXPECT_EQ(kTestGivenName, refreshed_account_info.given_name); + EXPECT_EQ(kTestLocale, refreshed_account_info.locale); + EXPECT_EQ(kTestPictureUrl, refreshed_account_info.picture_url); +} +#endif + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.cc b/chromium/components/signin/public/identity_manager/identity_test_environment.cc new file mode 100644 index 00000000000..6c033416f65 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.cc @@ -0,0 +1,505 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_test_environment.h" + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/image_fetcher/core/fake_image_decoder.h" +#include "components/signin/internal/identity_manager/account_fetcher_service.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h" +#include "components/signin/internal/identity_manager/diagnostics_provider_impl.h" +#include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#include "components/signin/internal/identity_manager/primary_account_mutator_impl.h" +#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/device_accounts_synchronizer.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/primary_account_mutator.h" +#include "components/signin/public/identity_manager/test_identity_manager_observer.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "google_apis/gaia/oauth2_access_token_consumer.h" + +#if defined(OS_IOS) +#include "components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h" +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h" +#endif + +#if !defined(OS_ANDROID) && !defined(OS_IOS) +#include "components/signin/internal/identity_manager/accounts_mutator_impl.h" +#endif + +namespace signin { + +class IdentityManagerDependenciesOwner { + public: + IdentityManagerDependenciesOwner( + sync_preferences::TestingPrefServiceSyncable* pref_service, + TestSigninClient* test_signin_client); + ~IdentityManagerDependenciesOwner(); + + sync_preferences::TestingPrefServiceSyncable* pref_service(); + + TestSigninClient* signin_client(); + + private: + // Depending on whether a |pref_service| instance is passed in + // the constructor, exactly one of these will be non-null. + std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> + owned_pref_service_; + sync_preferences::TestingPrefServiceSyncable* raw_pref_service_ = nullptr; + + std::unique_ptr<TestSigninClient> owned_signin_client_; + TestSigninClient* raw_signin_client_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(IdentityManagerDependenciesOwner); +}; + +IdentityManagerDependenciesOwner::IdentityManagerDependenciesOwner( + sync_preferences::TestingPrefServiceSyncable* pref_service_param, + TestSigninClient* signin_client_param) + : owned_pref_service_( + pref_service_param + ? nullptr + : std::make_unique< + sync_preferences::TestingPrefServiceSyncable>()), + raw_pref_service_(pref_service_param), + owned_signin_client_( + signin_client_param + ? nullptr + : std::make_unique<TestSigninClient>(pref_service())), + raw_signin_client_(signin_client_param) {} + +IdentityManagerDependenciesOwner::~IdentityManagerDependenciesOwner() = default; + +sync_preferences::TestingPrefServiceSyncable* +IdentityManagerDependenciesOwner::pref_service() { + DCHECK(raw_pref_service_ || owned_pref_service_); + DCHECK(!(raw_pref_service_ && owned_pref_service_)); + + return raw_pref_service_ ? raw_pref_service_ : owned_pref_service_.get(); +} + +TestSigninClient* IdentityManagerDependenciesOwner::signin_client() { + DCHECK(raw_signin_client_ || owned_signin_client_); + DCHECK(!(raw_signin_client_ && owned_signin_client_)); + + return raw_signin_client_ ? raw_signin_client_ : owned_signin_client_.get(); +} + +IdentityTestEnvironment::IdentityTestEnvironment( + network::TestURLLoaderFactory* test_url_loader_factory, + sync_preferences::TestingPrefServiceSyncable* pref_service, + AccountConsistencyMethod account_consistency, + TestSigninClient* test_signin_client) + : IdentityTestEnvironment( + std::make_unique<IdentityManagerDependenciesOwner>( + pref_service, + test_signin_client), + test_url_loader_factory, + account_consistency) { + DCHECK(!test_url_loader_factory || !test_signin_client); +} + +IdentityTestEnvironment::IdentityTestEnvironment( + IdentityManager* identity_manager) { + DCHECK(identity_manager); + raw_identity_manager_ = identity_manager; + Initialize(); +} + +void IdentityTestEnvironment::Initialize() { + DCHECK(base::ThreadTaskRunnerHandle::Get()) + << "IdentityTestEnvironment requires a properly set up task " + "environment. " + "If your test has an existing one, move it to be initialized before " + "IdentityTestEnvironment. Otherwise, use " + "base::test::ScopedTaskEnvironment."; + test_identity_manager_observer_ = + std::make_unique<TestIdentityManagerObserver>(this->identity_manager()); + this->identity_manager()->AddDiagnosticsObserver(this); +} + +IdentityTestEnvironment::IdentityTestEnvironment( + std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner, + network::TestURLLoaderFactory* test_url_loader_factory, + AccountConsistencyMethod account_consistency) { + dependencies_owner_ = std::move(dependencies_owner); + TestSigninClient* test_signin_client = dependencies_owner_->signin_client(); + if (test_url_loader_factory) + test_signin_client->OverrideTestUrlLoaderFactory(test_url_loader_factory); + + sync_preferences::TestingPrefServiceSyncable* test_pref_service = + dependencies_owner_->pref_service(); + + IdentityManager::RegisterProfilePrefs(test_pref_service->registry()); + IdentityManager::RegisterLocalStatePrefs(test_pref_service->registry()); + + owned_identity_manager_ = + BuildIdentityManagerForTests(test_signin_client, test_pref_service, + base::FilePath(), account_consistency); + + Initialize(); +} + +// static +std::unique_ptr<IdentityManager> +IdentityTestEnvironment::BuildIdentityManagerForTests( + SigninClient* signin_client, + PrefService* pref_service, + base::FilePath user_data_dir, + AccountConsistencyMethod account_consistency) { + auto account_tracker_service = std::make_unique<AccountTrackerService>(); + account_tracker_service->Initialize(pref_service, user_data_dir); + + auto token_service = + std::make_unique<FakeProfileOAuth2TokenService>(pref_service); + + auto account_fetcher_service = std::make_unique<AccountFetcherService>(); + account_fetcher_service->Initialize( + signin_client, token_service.get(), account_tracker_service.get(), + std::make_unique<image_fetcher::FakeImageDecoder>()); + + std::unique_ptr<PrimaryAccountPolicyManager> policy_manager; +#if !defined(OS_CHROMEOS) + policy_manager = + std::make_unique<PrimaryAccountPolicyManagerImpl>(signin_client); +#endif + std::unique_ptr<PrimaryAccountManager> primary_account_manager = + std::make_unique<PrimaryAccountManager>( + signin_client, token_service.get(), account_tracker_service.get(), + account_consistency, std::move(policy_manager)); + primary_account_manager->Initialize(pref_service); + + std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service = + std::make_unique<GaiaCookieManagerService>(token_service.get(), + signin_client); + + std::unique_ptr<PrimaryAccountMutator> primary_account_mutator = + std::make_unique<PrimaryAccountMutatorImpl>(account_tracker_service.get(), + primary_account_manager.get(), + pref_service); + + std::unique_ptr<AccountsMutator> accounts_mutator; +#if !defined(OS_ANDROID) && !defined(OS_IOS) + accounts_mutator = std::make_unique<AccountsMutatorImpl>( + token_service.get(), account_tracker_service.get(), + primary_account_manager.get(), pref_service); +#endif + + auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>( + 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()); + + std::unique_ptr<DeviceAccountsSynchronizer> device_accounts_synchronizer; +#if defined(OS_IOS) + device_accounts_synchronizer = + std::make_unique<DeviceAccountsSynchronizerImpl>( + token_service->GetDelegate()); +#endif + + return std::make_unique<IdentityManager>( + std::move(account_tracker_service), std::move(token_service), + std::move(gaia_cookie_manager_service), + std::move(primary_account_manager), std::move(account_fetcher_service), + std::move(primary_account_mutator), std::move(accounts_mutator), + std::move(accounts_cookie_mutator), std::move(diagnostics_provider), + std::move(device_accounts_synchronizer)); +} + +IdentityTestEnvironment::~IdentityTestEnvironment() { + // Remove the Observer that IdentityTestEnvironment added during its + // initialization. + identity_manager()->RemoveDiagnosticsObserver(this); +} + +IdentityManager* IdentityTestEnvironment::identity_manager() { + DCHECK(raw_identity_manager_ || owned_identity_manager_); + DCHECK(!(raw_identity_manager_ && owned_identity_manager_)); + + return raw_identity_manager_ ? raw_identity_manager_ + : owned_identity_manager_.get(); +} + +TestIdentityManagerObserver* +IdentityTestEnvironment::identity_manager_observer() { + return test_identity_manager_observer_.get(); +} + +CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount( + const std::string& email) { + return signin::SetPrimaryAccount(identity_manager(), email); +} + +void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() { + signin::SetRefreshTokenForPrimaryAccount(identity_manager()); +} + +void IdentityTestEnvironment::SetInvalidRefreshTokenForPrimaryAccount() { + signin::SetInvalidRefreshTokenForPrimaryAccount(identity_manager()); +} + +void IdentityTestEnvironment::RemoveRefreshTokenForPrimaryAccount() { + signin::RemoveRefreshTokenForPrimaryAccount(identity_manager()); +} + +AccountInfo IdentityTestEnvironment::MakePrimaryAccountAvailable( + const std::string& email) { + return signin::MakePrimaryAccountAvailable(identity_manager(), email); +} + +void IdentityTestEnvironment::ClearPrimaryAccount( + ClearPrimaryAccountPolicy policy) { + signin::ClearPrimaryAccount(identity_manager(), policy); +} + +AccountInfo IdentityTestEnvironment::MakeAccountAvailable( + const std::string& email) { + return signin::MakeAccountAvailable(identity_manager(), email); +} + +void IdentityTestEnvironment::SetRefreshTokenForAccount( + const CoreAccountId& account_id) { + return signin::SetRefreshTokenForAccount(identity_manager(), account_id); +} + +void IdentityTestEnvironment::SetInvalidRefreshTokenForAccount( + const CoreAccountId& account_id) { + return signin::SetInvalidRefreshTokenForAccount(identity_manager(), + account_id); +} + +void IdentityTestEnvironment::RemoveRefreshTokenForAccount( + const CoreAccountId& account_id) { + return signin::RemoveRefreshTokenForAccount(identity_manager(), account_id); +} + +void IdentityTestEnvironment::UpdatePersistentErrorOfRefreshTokenForAccount( + const CoreAccountId& account_id, + const GoogleServiceAuthError& auth_error) { + return signin::UpdatePersistentErrorOfRefreshTokenForAccount( + identity_manager(), account_id, auth_error); +} + +void IdentityTestEnvironment::SetCookieAccounts( + const std::vector<CookieParamsForTest>& cookie_accounts) { + signin::SetCookieAccounts( + identity_manager(), + dependencies_owner_->signin_client()->GetTestURLLoaderFactory(), + cookie_accounts); +} + +void IdentityTestEnvironment::SetAutomaticIssueOfAccessTokens(bool grant) { + fake_token_service()->set_auto_post_fetch_response_on_message_loop(grant); +} + +void IdentityTestEnvironment:: + WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + const std::string& token, + const base::Time& expiration, + const std::string& id_token) { + WaitForAccessTokenRequestIfNecessary(base::nullopt); + fake_token_service()->IssueTokenForAllPendingRequests( + OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token)); +} + +void IdentityTestEnvironment:: + WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + const CoreAccountId& account_id, + const std::string& token, + const base::Time& expiration, + const std::string& id_token) { + WaitForAccessTokenRequestIfNecessary(account_id); + fake_token_service()->IssueAllTokensForAccount( + account_id, + OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token)); +} + +void IdentityTestEnvironment:: + WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes( + const std::string& token, + const base::Time& expiration, + const std::string& id_token, + const identity::ScopeSet& scopes) { + WaitForAccessTokenRequestIfNecessary(base::nullopt); + fake_token_service()->IssueTokenForScope( + scopes, + OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token)); +} + +void IdentityTestEnvironment:: + WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + const GoogleServiceAuthError& error) { + WaitForAccessTokenRequestIfNecessary(base::nullopt); + fake_token_service()->IssueErrorForAllPendingRequests(error); +} + +void IdentityTestEnvironment:: + WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error) { + WaitForAccessTokenRequestIfNecessary(account_id); + fake_token_service()->IssueErrorForAllPendingRequestsForAccount(account_id, + error); +} + +void IdentityTestEnvironment::SetCallbackForNextAccessTokenRequest( + base::OnceClosure callback) { + on_access_token_requested_callback_ = std::move(callback); +} + +IdentityTestEnvironment::AccessTokenRequestState::AccessTokenRequestState() = + default; +IdentityTestEnvironment::AccessTokenRequestState::~AccessTokenRequestState() = + default; +IdentityTestEnvironment::AccessTokenRequestState::AccessTokenRequestState( + AccessTokenRequestState&& other) = default; +IdentityTestEnvironment::AccessTokenRequestState& +IdentityTestEnvironment::AccessTokenRequestState::operator=( + AccessTokenRequestState&& other) = default; + +void IdentityTestEnvironment::OnAccessTokenRequested( + const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::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 + // of an invocation of WaitForAccessTokenRequestIfNecessary() that will be + // made in this same iteration of the run loop. + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&IdentityTestEnvironment::HandleOnAccessTokenRequested, + weak_ptr_factory_.GetWeakPtr(), account_id)); +} + +void IdentityTestEnvironment::HandleOnAccessTokenRequested( + CoreAccountId account_id) { + if (on_access_token_requested_callback_) { + std::move(on_access_token_requested_callback_).Run(); + return; + } + + for (auto it = requesters_.begin(); it != requesters_.end(); ++it) { + if (!it->account_id || (it->account_id.value() == account_id)) { + if (it->state == AccessTokenRequestState::kAvailable) + return; + if (it->on_available) + std::move(it->on_available).Run(); + requesters_.erase(it); + return; + } + } + + // A requests came in for a request for which we are not waiting. Record + // that it's available. + requesters_.emplace_back(); + requesters_.back().state = AccessTokenRequestState::kAvailable; + requesters_.back().account_id = account_id; +} + +void IdentityTestEnvironment::WaitForAccessTokenRequestIfNecessary( + base::Optional<CoreAccountId> account_id) { + // Handle HandleOnAccessTokenRequested getting called before + // WaitForAccessTokenRequestIfNecessary. + if (account_id) { + for (auto it = requesters_.begin(); it != requesters_.end(); ++it) { + if (it->account_id && it->account_id.value() == account_id.value()) { + // Can't wait twice for same thing. + DCHECK_EQ(AccessTokenRequestState::kAvailable, it->state); + requesters_.erase(it); + return; + } + } + } else { + for (auto it = requesters_.begin(); it != requesters_.end(); ++it) { + if (it->state == AccessTokenRequestState::kAvailable) { + requesters_.erase(it); + return; + } + } + } + + base::RunLoop run_loop; + requesters_.emplace_back(); + requesters_.back().state = AccessTokenRequestState::kPending; + requesters_.back().account_id = std::move(account_id); + requesters_.back().on_available = run_loop.QuitClosure(); + run_loop.Run(); +} + +FakeProfileOAuth2TokenService* IdentityTestEnvironment::fake_token_service() { + // We can't absolutely guarantee that IdentityTestEnvironment was not given an + // IdentityManager that uses a non-fake FakeProfileOAuth2TokenService. If that + // ever happens, this will blow up. There doesn't seem to be a better option. + return static_cast<FakeProfileOAuth2TokenService*>( + identity_manager()->GetTokenService()); +} + +void IdentityTestEnvironment::UpdateAccountInfoForAccount( + AccountInfo account_info) { + signin::UpdateAccountInfoForAccount(identity_manager(), account_info); +} + +void IdentityTestEnvironment::ResetToAccountsNotYetLoadedFromDiskState() { + fake_token_service()->set_all_credentials_loaded_for_testing(false); +} + +void IdentityTestEnvironment::ReloadAccountsFromDisk() { + fake_token_service()->LoadCredentials(""); +} + +bool IdentityTestEnvironment::IsAccessTokenRequestPending() { + return fake_token_service()->GetPendingRequests().size(); +} + +void IdentityTestEnvironment::SetFreshnessOfAccountsInGaiaCookie( + bool accounts_are_fresh) { + signin::SetFreshnessOfAccountsInGaiaCookie(identity_manager(), + accounts_are_fresh); +} + +void IdentityTestEnvironment::EnableRemovalOfExtendedAccountInfo() { + identity_manager()->GetAccountFetcherService()->EnableAccountRemovalForTest(); +} + +void IdentityTestEnvironment::SimulateSuccessfulFetchOfAccountInfo( + const CoreAccountId& account_id, + const std::string& email, + const std::string& gaia, + const std::string& hosted_domain, + const std::string& full_name, + const std::string& given_name, + const std::string& locale, + const std::string& picture_url) { + signin::SimulateSuccessfulFetchOfAccountInfo( + identity_manager(), account_id, email, gaia, hosted_domain, full_name, + given_name, locale, picture_url); +} + +void IdentityTestEnvironment::SimulateMergeSessionFailure( + const GoogleServiceAuthError& auth_error) { + // GaiaCookieManagerService changes the visibility of inherited method + // OnMergeSessionFailure from public to private. Cast to a base class + // pointer to call the method. + static_cast<GaiaAuthConsumer*>( + identity_manager()->GetGaiaCookieManagerService()) + ->OnMergeSessionFailure(auth_error); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.h b/chromium/components/signin/public/identity_manager/identity_test_environment.h new file mode 100644 index 00000000000..3b5efc73fbf --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.h @@ -0,0 +1,370 @@ +// 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_IDENTITY_TEST_ENVIRONMENT_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_TEST_ENVIRONMENT_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/optional.h" +#include "build/build_config.h" +#include "components/signin/public/base/account_consistency_method.h" +#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" + +class FakeProfileOAuth2TokenService; +class IdentityTestEnvironmentProfileAdaptor; +class PrefService; +class TestSigninClient; + +namespace sync_preferences { +class TestingPrefServiceSyncable; +} + +namespace network { +class TestURLLoaderFactory; +} + +namespace signin { + +class IdentityManagerDependenciesOwner; +class TestIdentityManagerObserver; + +// Class that creates an IdentityManager for use in testing contexts and +// provides facilities for driving that IdentityManager. The IdentityManager +// instance is brought up in an environment where the primary account is +// not available; call MakePrimaryAccountAvailable() as needed. +// NOTE: IdentityTestEnvironment requires that tests have a properly set up +// task environment. If your test doesn't already have one, use a +// base::test::ScopedTaskEnvironment instance variable to fulfill this +// requirement. +class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver { + public: + // Preferred constructor: constructs an IdentityManager object and its + // dependencies internally. Cannot be used if the client of this class + // is still interacting directly with those dependencies (e.g., if + // IdentityTestEnvironment is being introduced to incrementally convert + // a test). In that case, use the below constructor and switch to this + // constructor once the conversion is complete. + // + // This constructor takes an optional parameter |test_url_loader_factory| to + // use for cookie-related network requests. + // Note: the provided |test_url_loader_factory| is expected to outlive + // IdentityTestEnvironment. + // + // This constructor also takes an optional PrefService instance as parameter, + // which allows tests to move away from referencing IdentityManager's + // dependencies directly (namely AccountTrackerService, PO2TS), but still be + // able to tweak preferences on demand. + // + // |account_consistency| specifies the account consistency policy that will be + // used. + // + // A specific TestSigninClient instance can be passed optionally. If it is + // null, the test environment will automatically build one internally. + // + // Note: at least one of |test_url_loader_factory| and |test_signin_client| + // must be nulltpr. They cannot both be specified at the same time. + IdentityTestEnvironment( + network::TestURLLoaderFactory* test_url_loader_factory = nullptr, + sync_preferences::TestingPrefServiceSyncable* pref_service = nullptr, + AccountConsistencyMethod account_consistency = + AccountConsistencyMethod::kDisabled, + TestSigninClient* test_signin_client = nullptr); + + ~IdentityTestEnvironment() override; + + // The IdentityManager instance associated with this instance. + IdentityManager* identity_manager(); + + // Returns the |TestIdentityManagerObserver| watching the IdentityManager. + TestIdentityManagerObserver* identity_manager_observer(); + + // Sets the primary account for the given email address, generating a GAIA ID + // that corresponds uniquely to that email address. On non-ChromeOS, results + // in the firing of the IdentityManager and PrimaryAccountManager callbacks + // for signin success. Blocks until the primary account is set. Returns the + // CoreAccountInfo of the newly-set account. + CoreAccountInfo SetPrimaryAccount(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 + // |identity_manager|. + void SetRefreshTokenForPrimaryAccount(); + + // Sets a special invalid 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 |identity_manager|. + void SetInvalidRefreshTokenForPrimaryAccount(); + + // Removes any refresh token for the primary account, if present. Blocks until + // the refresh token is removed. + void RemoveRefreshTokenForPrimaryAccount(); + + // Makes the primary account available for the given email address, generating + // a GAIA ID and refresh token that correspond uniquely to that email address. + // On non-ChromeOS platforms, this will also result in the firing of the + // IdentityManager and PrimaryAccountManager callbacks for signin success. On + // all platforms, this method blocks until the primary account is available. + // Returns the AccountInfo of the newly-available account. + AccountInfo MakePrimaryAccountAvailable(const std::string& email); + + // Clears the primary account if present, with |policy| used to determine + // whether to keep or remove all accounts. On non-ChromeOS, results in the + // firing of the IdentityManager and PrimaryAccountManager callbacks for + // signout. Blocks until the primary account is cleared. + void ClearPrimaryAccount( + ClearPrimaryAccountPolicy policy = ClearPrimaryAccountPolicy::DEFAULT); + + // Makes an account available for the given email address, generating a GAIA + // ID and refresh token that correspond uniquely to that email address. Blocks + // until the account is available. Returns the AccountInfo of the + // newly-available account. + AccountInfo MakeAccountAvailable(const std::string& email); + + // Sets a refresh token for the given account (which must already be + // available). Before updating the refresh token, blocks until refresh tokens + // are loaded. After updating the token, blocks until the update is processed + // by |identity_manager|. NOTE: See disclaimer at top of file re: direct + // usage. + void SetRefreshTokenForAccount(const CoreAccountId& account_id); + + // Sets a special invalid refresh token for the given account (which must + // already be available). Before updating the refresh token, blocks until + // refresh tokens are loaded. After updating the token, blocks until the + // update is processed by |identity_manager|. NOTE: See disclaimer at top of + // file re: direct usage. + void SetInvalidRefreshTokenForAccount(const CoreAccountId& account_id); + + // 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. + void RemoveRefreshTokenForAccount(const CoreAccountId& account_id); + + // Updates the persistent auth error set on |account_id| which must be a known + // account, i.e., an account with a refresh token. + void UpdatePersistentErrorOfRefreshTokenForAccount( + const CoreAccountId& account_id, + const GoogleServiceAuthError& auth_error); + + // Puts the given accounts into the Gaia cookie, replacing any previous + // accounts. Blocks until the accounts have been set. + void SetCookieAccounts( + const std::vector<CookieParamsForTest>& cookie_accounts); + + // When this is set, access token requests will be automatically granted with + // an access token value of "access_token". + void SetAutomaticIssueOfAccessTokens(bool grant); + + // Issues |token| 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 + // other WaitFor... method, or (b) will occur in the future. In the latter + // case, waits until the access token request occurs. + // |id_token| is an uncommonly-needed parameter that contains extra + // information regarding the user's currently-registered services; if this + // means nothing to you, you don't need to concern yourself with it. + // NOTE: This method behaves this way to allow IdentityTestEnvironment to be + // agnostic with respect to whether access token requests are handled + // synchronously or asynchronously in the production code. + // NOTE: This version is suitable for use in the common context where access + // token requests are only being made for one account. If you need to + // disambiguate requests coming for different accounts, see the version below. + void WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + const std::string& token, + const base::Time& expiration, + const std::string& id_token = std::string()); + + // Issues |token| in response to an access token request for |account_id| that + // either already occurred and has not been matched by a previous call to this + // or other WaitFor... method , or (b) will occur in the future. In the latter + // case, waits until the access token request occurs. + // |id_token| is an uncommonly-needed parameter that contains extra + // information regarding the user's currently-registered services; if this + // means nothing to you, you don't need to concern yourself with it. + // NOTE: This method behaves this way to allow + // IdentityTestEnvironment to be agnostic with respect to whether access token + // requests are handled synchronously or asynchronously in the production + // code. + void WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + const CoreAccountId& account_id, + const std::string& token, + const base::Time& expiration, + const std::string& id_token = std::string()); + + // Similar to WaitForAccessTokenRequestIfNecessaryAndRespondWithToken above + // apart from the fact that it issues tokens for a given set of scopes only, + // instead of issueing all tokens for all requests (the method variant above). + void WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes( + const std::string& token, + const base::Time& expiration, + const std::string& id_token, + const identity::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 + // other WaitFor... method, or (b) will occur in the future. In the latter + // case, waits until the access token request occurs. + // NOTE: This method behaves this way to allow IdentityTestEnvironment to be + // agnostic with respect to whether access token requests are handled + // synchronously or asynchronously in the production code. + // NOTE: This version is suitable for use in the common context where access + // token requests are only being made for one account. If you need to + // disambiguate requests coming for different accounts, see the version below. + void WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + const GoogleServiceAuthError& error); + + // Issues |error| in response to an access token request for |account_id| that + // either has (a) already occurred and has not been matched by a previous call + // to this or other WaitFor... method, or (b) will occur in the future. In the + // latter case, waits until the access token request occurs. + // NOTE: This method behaves this way to allow + // IdentityTestEnvironment to be agnostic with respect to whether access token + // requests are handled synchronously or asynchronously in the production + // code. + void WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + const CoreAccountId& account_id, + const GoogleServiceAuthError& error); + + // Sets a callback that will be invoked on the next incoming access token + // request. Note that this can not be combined with the + // WaitForAccessTokenRequestIfNecessaryAndRespondWith* methods - you must + // either wait for the callback to get called, or explicitly reset it by + // passing in a null callback, before the Wait* methods can be used again. + void SetCallbackForNextAccessTokenRequest(base::OnceClosure callback); + + // Updates the info for |account_info.account_id|, which must be a known + // account. + void UpdateAccountInfoForAccount(AccountInfo account_info); + + // Resets to the state where accounts have not yet been loaded from disk. + void ResetToAccountsNotYetLoadedFromDiskState(); + + // Simulates the reloading of the accounts from disk. + void ReloadAccountsFromDisk(); + + // Returns whether there is a access token request pending. + bool IsAccessTokenRequestPending(); + + // Sets whether the list of accounts in Gaia cookie jar is fresh and does not + // need to be updated. + void SetFreshnessOfAccountsInGaiaCookie(bool accounts_are_fresh); + + // By default, extended account info removal is disabled in testing + // contexts. This call enables it for tests that require + // IdentityManager::Observer::OnExtendedAccountInfoRemoved() to fire as + // expected. TODO(https://crbug.com/927687): Enable this unconditionally. + void EnableRemovalOfExtendedAccountInfo(); + + // Simulate account fetching using AccountTrackerService without sending + // network requests. + void SimulateSuccessfulFetchOfAccountInfo(const CoreAccountId& account_id, + const std::string& email, + const std::string& gaia, + const std::string& hosted_domain, + const std::string& full_name, + const std::string& given_name, + const std::string& locale, + const std::string& picture_url); + + // Simulates a merge session failure with |auth_error| as the error. + void SimulateMergeSessionFailure(const GoogleServiceAuthError& auth_error); + + private: + friend class ::IdentityTestEnvironmentProfileAdaptor; + + struct AccessTokenRequestState { + AccessTokenRequestState(); + ~AccessTokenRequestState(); + AccessTokenRequestState(AccessTokenRequestState&& other); + AccessTokenRequestState& operator=(AccessTokenRequestState&& other); + + enum { + kPending, + kAvailable, + } state; + base::Optional<std::string> account_id; + base::OnceClosure on_available; + }; + + // Constructs an IdentityTestEnvironment that builds and owns an + // IdentityManager. This private constructor is meant to only be called + // internally by IdentityTestEnvironment from other constructors. + IdentityTestEnvironment( + std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner, + network::TestURLLoaderFactory* test_url_loader_factory, + AccountConsistencyMethod account_consistency); + + // Constructs an IdentityTestEnvironment that uses the supplied + // |identity_manager|. + // For use only in contexts where IdentityManager and its dependencies are all + // unavoidably created by the embedder (e.g., //chrome-level unittests that + // use the ProfileKeyedServiceFactory infrastructure). + // NOTE: This constructor is for usage only in the special case of embedder + // unittests that must use the IdentityManager instance associated with the + // Profile. If you think you have another use case for it, contact + // blundell@chromium.org. + IdentityTestEnvironment(IdentityManager* identity_manager); + + // IdentityManager::DiagnosticsObserver: + void OnAccessTokenRequested(const CoreAccountId& account_id, + const std::string& consumer_id, + const identity::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 + // is non-null *and* either |*pending_access_token_requester_| equals + // |account_id| or |pending_access_token_requester_| is empty. + void HandleOnAccessTokenRequested(CoreAccountId account_id); + + // If a token request for |account_id| (or any account if nullopt) has already + // been made and not matched by a different call, returns immediately. + // Otherwise and runs a nested runloop until a matching access token request + // is observed. + void WaitForAccessTokenRequestIfNecessary( + base::Optional<CoreAccountId> account_id); + + // Returns the FakeProfileOAuth2TokenService owned by IdentityManager. + FakeProfileOAuth2TokenService* fake_token_service(); + + // Owner of all dependencies that don't belong to IdentityManager. + std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner_; + + // This will be null if a TestSigninClient was provided to + // IdentityTestEnvironment's constructor. + std::unique_ptr<TestSigninClient> owned_signin_client_; + + // Depending on which constructor is used, exactly one of these will be + // non-null. See the documentation on the constructor wherein IdentityManager + // is passed in for required lifetime invariants in that case. + std::unique_ptr<IdentityManager> owned_identity_manager_; + IdentityManager* raw_identity_manager_ = nullptr; + + std::unique_ptr<TestIdentityManagerObserver> test_identity_manager_observer_; + + base::OnceClosure on_access_token_requested_callback_; + std::vector<AccessTokenRequestState> requesters_; + + // Create an IdentityManager instance for tests. + static std::unique_ptr<IdentityManager> BuildIdentityManagerForTests( + SigninClient* signin_client, + PrefService* pref_service, + base::FilePath user_data_dir, + AccountConsistencyMethod account_consistency = + AccountConsistencyMethod::kDisabled); + + // Shared constructor initialization logic. + void Initialize(); + + base::WeakPtrFactory<IdentityTestEnvironment> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(IdentityTestEnvironment); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_TEST_ENVIRONMENT_H_ diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment_unittest.cc b/chromium/components/signin/public/identity_manager/identity_test_environment_unittest.cc new file mode 100644 index 00000000000..9c03209a972 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_test_environment_unittest.cc @@ -0,0 +1,57 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_test_environment.h" + +#include "base/bind.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/public/identity_manager/access_token_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace signin { + +class IdentityTestEnvironmentTest : public testing::Test { + public: + IdentityTestEnvironmentTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT, + base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode:: + QUEUED) {} + + ~IdentityTestEnvironmentTest() override { + scoped_task_environment_.RunUntilIdle(); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + DISALLOW_COPY_AND_ASSIGN(IdentityTestEnvironmentTest); +}; + +TEST_F(IdentityTestEnvironmentTest, + IdentityTestEnvironmentCancelsPendingRequestsOnDestruction) { + std::unique_ptr<IdentityTestEnvironment> identity_test_environment = + std::make_unique<IdentityTestEnvironment>(); + + identity_test_environment->MakePrimaryAccountAvailable("primary@example.com"); + AccessTokenFetcher::TokenCallback callback = base::BindOnce( + [](GoogleServiceAuthError error, AccessTokenInfo access_token_info) {}); + std::set<std::string> scopes{"scope"}; + + std::unique_ptr<AccessTokenFetcher> fetcher = + identity_test_environment->identity_manager() + ->CreateAccessTokenFetcherForAccount( + identity_test_environment->identity_manager() + ->GetPrimaryAccountId(), + "dummy_consumer", scopes, std::move(callback), + AccessTokenFetcher::Mode::kImmediate); + + // Deleting the IdentityTestEnvironment should cancel any pending + // task in order to avoid use-after-free crashes. The destructor of + // the test will spin the runloop which would run + // IdentityTestEnvironment pending tasks if not canceled. + identity_test_environment.reset(); + fetcher.reset(); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.cc b/chromium/components/signin/public/identity_manager/identity_test_utils.cc new file mode 100644 index 00000000000..1cbd39b19bf --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.cc @@ -0,0 +1,376 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_test_utils.h" + +#include <vector> + +#include "base/run_loop.h" +#include "build/build_config.h" +#include "components/signin/internal/identity_manager/account_tracker_service.h" +#include "components/signin/internal/identity_manager/gaia_cookie_manager_service.h" +#include "components/signin/internal/identity_manager/primary_account_manager.h" +#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/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" +#endif + +namespace signin { + +namespace { + +void WaitForLoadCredentialsToComplete(IdentityManager* identity_manager) { + base::RunLoop run_loop; + TestIdentityManagerObserver load_credentials_observer(identity_manager); + load_credentials_observer.SetOnRefreshTokensLoadedCallback( + run_loop.QuitClosure()); + + if (identity_manager->AreRefreshTokensLoaded()) + return; + + // Do NOT explicitly load credentials here: + // 1. It is not re-entrant and will DCHECK fail. + // 2. It should have been called by IdentityManager during its initialization. + + run_loop.Run(); +} + +// Helper function that updates the refresh token for |account_id| to +// |new_token|. Before updating the refresh token, blocks until refresh tokens +// are loaded. After updating the token, blocks until the update is processed by +// |identity_manager|. +void UpdateRefreshTokenForAccount( + ProfileOAuth2TokenService* token_service, + AccountTrackerService* account_tracker_service, + IdentityManager* identity_manager, + const std::string& account_id, + const std::string& new_token) { + DCHECK_EQ(account_tracker_service->GetAccountInfo(account_id).account_id, + account_id) + << "To set the refresh token for an unknown account, use " + "MakeAccountAvailable()"; + + // Ensure that refresh tokens are loaded; some platforms enforce the invariant + // that refresh token mutation cannot occur until refresh tokens are loaded, + // and it is desired to eventually enforce that invariant across all + // platforms. + WaitForLoadCredentialsToComplete(identity_manager); + + base::RunLoop run_loop; + TestIdentityManagerObserver token_updated_observer(identity_manager); + token_updated_observer.SetOnRefreshTokenUpdatedCallback( + run_loop.QuitClosure()); + + token_service->UpdateCredentials(account_id, new_token); + + run_loop.Run(); +} + +} // namespace + +CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, + const std::string& email) { + DCHECK(!identity_manager->HasPrimaryAccount()); + PrimaryAccountManager* primary_account_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()); + + primary_account_manager->SignIn(email); + + DCHECK(primary_account_manager->IsAuthenticated()); + DCHECK(identity_manager->HasPrimaryAccount()); + return identity_manager->GetPrimaryAccountInfo(); +} + +void SetRefreshTokenForPrimaryAccount(IdentityManager* identity_manager, + const std::string& token_value) { + DCHECK(identity_manager->HasPrimaryAccount()); + std::string account_id = identity_manager->GetPrimaryAccountId(); + SetRefreshTokenForAccount(identity_manager, account_id, token_value); +} + +void SetInvalidRefreshTokenForPrimaryAccount( + IdentityManager* identity_manager) { + DCHECK(identity_manager->HasPrimaryAccount()); + std::string account_id = identity_manager->GetPrimaryAccountId(); + + SetInvalidRefreshTokenForAccount(identity_manager, account_id); +} + +void RemoveRefreshTokenForPrimaryAccount(IdentityManager* identity_manager) { + if (!identity_manager->HasPrimaryAccount()) + return; + + std::string account_id = identity_manager->GetPrimaryAccountId(); + + RemoveRefreshTokenForAccount(identity_manager, account_id); +} + +AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager, + const std::string& email) { + CoreAccountInfo account_info = SetPrimaryAccount(identity_manager, email); + SetRefreshTokenForPrimaryAccount(identity_manager); + base::Optional<AccountInfo> primary_account_info = + identity_manager->FindAccountInfoForAccountWithRefreshTokenByAccountId( + account_info.account_id); + // Ensure that extended information for the account is available after setting + // the refresh token. + DCHECK(primary_account_info.has_value()); + return primary_account_info.value(); +} + +void ClearPrimaryAccount(IdentityManager* identity_manager, + ClearPrimaryAccountPolicy policy) { +#if defined(OS_CHROMEOS) + // TODO(blundell): If we ever need this functionality on ChromeOS (which seems + // unlikely), plumb this through to just clear the primary account info + // synchronously with IdentityManager. + NOTREACHED(); +#else + if (!identity_manager->HasPrimaryAccount()) + return; + + base::RunLoop run_loop; + TestIdentityManagerObserver signout_observer(identity_manager); + signout_observer.SetOnPrimaryAccountClearedCallback(run_loop.QuitClosure()); + + PrimaryAccountManager* primary_account_manager = + identity_manager->GetPrimaryAccountManager(); + signin_metrics::ProfileSignout signout_source_metric = + signin_metrics::SIGNOUT_TEST; + signin_metrics::SignoutDelete signout_delete_metric = + signin_metrics::SignoutDelete::IGNORE_METRIC; + + switch (policy) { + case ClearPrimaryAccountPolicy::DEFAULT: + primary_account_manager->SignOut(signout_source_metric, + signout_delete_metric); + break; + case ClearPrimaryAccountPolicy::KEEP_ALL_ACCOUNTS: + primary_account_manager->SignOutAndKeepAllAccounts(signout_source_metric, + signout_delete_metric); + break; + case ClearPrimaryAccountPolicy::REMOVE_ALL_ACCOUNTS: + primary_account_manager->SignOutAndRemoveAllAccounts( + signout_source_metric, signout_delete_metric); + break; + } + + run_loop.Run(); +#endif +} + +AccountInfo MakeAccountAvailable(IdentityManager* identity_manager, + const std::string& email) { + AccountTrackerService* account_tracker_service = + identity_manager->GetAccountTrackerService(); + + DCHECK(account_tracker_service); + DCHECK(account_tracker_service->FindAccountInfoByEmail(email).IsEmpty()); + + // Wait until tokens are loaded, otherwise the account will be removed as soon + // as tokens finish loading. + WaitForLoadCredentialsToComplete(identity_manager); + + std::string gaia_id = GetTestGaiaIdForEmail(email); + account_tracker_service->SeedAccountInfo(gaia_id, email); + + AccountInfo account_info = + account_tracker_service->FindAccountInfoByEmail(email); + DCHECK(!account_info.account_id.empty()); + + SetRefreshTokenForAccount(identity_manager, account_info.account_id); + + return account_info; +} + +AccountInfo MakeAccountAvailableWithCookies( + IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::string& email, + const std::string& gaia_id) { + AccountTrackerService* account_tracker_service = + identity_manager->GetAccountTrackerService(); + + DCHECK(account_tracker_service); + DCHECK(account_tracker_service->FindAccountInfoByEmail(email).IsEmpty()); + + // Wait until tokens are loaded, otherwise the account will be removed as soon + // as tokens finish loading. + WaitForLoadCredentialsToComplete(identity_manager); + + SetCookieAccounts(identity_manager, test_url_loader_factory, + {{email, gaia_id}}); + + account_tracker_service->SeedAccountInfo(gaia_id, email); + + AccountInfo account_info = + account_tracker_service->FindAccountInfoByEmail(email); + DCHECK(!account_info.account_id.empty()); + + SetRefreshTokenForAccount(identity_manager, account_info.account_id); + + return account_info; +} + +void SetRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id, + const std::string& token_value) { + UpdateRefreshTokenForAccount( + identity_manager->GetTokenService(), + identity_manager->GetAccountTrackerService(), identity_manager, + account_id, + token_value.empty() ? "refresh_token_for_" + account_id : token_value); +} + +void SetInvalidRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id) { + UpdateRefreshTokenForAccount(identity_manager->GetTokenService(), + + identity_manager->GetAccountTrackerService(), + identity_manager, account_id, + GaiaConstants::kInvalidRefreshToken); +} + +void RemoveRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id) { + if (!identity_manager->HasAccountWithRefreshToken(account_id)) + return; + + base::RunLoop run_loop; + TestIdentityManagerObserver token_updated_observer(identity_manager); + token_updated_observer.SetOnRefreshTokenRemovedCallback( + run_loop.QuitClosure()); + + identity_manager->GetTokenService()->RevokeCredentials(account_id); + + run_loop.Run(); +} + +void SetCookieAccounts( + IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::vector<CookieParamsForTest>& cookie_accounts) { + // Convert |cookie_accounts| to the format list_accounts_test_utils wants. + std::vector<CookieParams> gaia_cookie_accounts; + for (const CookieParamsForTest& params : cookie_accounts) { + gaia_cookie_accounts.push_back({params.email, params.gaia_id, + /*valid=*/true, /*signed_out=*/false, + /*verified=*/true}); + } + + base::RunLoop run_loop; + TestIdentityManagerObserver cookie_observer(identity_manager); + cookie_observer.SetOnAccountsInCookieUpdatedCallback(run_loop.QuitClosure()); + + SetListAccountsResponseWithParams(gaia_cookie_accounts, + test_url_loader_factory); + + GaiaCookieManagerService* cookie_manager = + identity_manager->GetGaiaCookieManagerService(); + cookie_manager->set_list_accounts_stale_for_testing(true); + cookie_manager->ListAccounts(nullptr, nullptr); + + run_loop.Run(); +} + +void UpdateAccountInfoForAccount(IdentityManager* identity_manager, + AccountInfo account_info) { + // Make sure the account being updated is a known account. + + AccountTrackerService* account_tracker_service = + identity_manager->GetAccountTrackerService(); + + DCHECK(account_tracker_service); + DCHECK(!account_tracker_service->GetAccountInfo(account_info.account_id) + .account_id.empty()); + + account_tracker_service->SeedAccountInfo(account_info); +} + +void SetFreshnessOfAccountsInGaiaCookie(IdentityManager* identity_manager, + bool accounts_are_fresh) { + GaiaCookieManagerService* cookie_manager = + identity_manager->GetGaiaCookieManagerService(); + cookie_manager->set_list_accounts_stale_for_testing(!accounts_are_fresh); +} + +std::string GetTestGaiaIdForEmail(const std::string& email) { + std::string gaia_id = + std::string("gaia_id_for_") + gaia::CanonicalizeEmail(email); + // Avoid character '@' in the gaia ID string as there is code in the codebase + // that asserts that a gaia ID does not contain a "@" character. + std::replace(gaia_id.begin(), gaia_id.end(), '@', '_'); + return gaia_id; +} + +void UpdatePersistentErrorOfRefreshTokenForAccount( + IdentityManager* identity_manager, + const std::string& account_id, + const GoogleServiceAuthError& auth_error) { + DCHECK(identity_manager->HasAccountWithRefreshToken(account_id)); + identity_manager->GetTokenService()->GetDelegate()->UpdateAuthError( + account_id, auth_error); +} + +void DisableAccessTokenFetchRetries(IdentityManager* identity_manager) { + identity_manager->GetTokenService() + ->set_max_authorization_token_fetch_retries_for_testing(0); +} + +#if defined(OS_ANDROID) +void DisableInteractionWithSystemAccounts() { + OAuth2TokenServiceDelegateAndroid:: + set_disable_interaction_with_system_accounts(); +} +#endif + +void CancelAllOngoingGaiaCookieOperations(IdentityManager* identity_manager) { + identity_manager->GetGaiaCookieManagerService()->CancelAll(); +} + +void SimulateSuccessfulFetchOfAccountInfo(IdentityManager* identity_manager, + const std::string& account_id, + const std::string& email, + const std::string& gaia, + const std::string& hosted_domain, + const std::string& full_name, + const std::string& given_name, + const std::string& locale, + const std::string& picture_url) { + base::DictionaryValue user_info; + user_info.SetString("id", gaia); + user_info.SetString("email", email); + user_info.SetString("hd", hosted_domain); + user_info.SetString("name", full_name); + user_info.SetString("given_name", given_name); + user_info.SetString("locale", locale); + user_info.SetString("picture", picture_url); + + AccountTrackerService* account_tracker_service = + identity_manager->GetAccountTrackerService(); + account_tracker_service->SetAccountInfoFromUserInfo(account_id, &user_info); +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.h b/chromium/components/signin/public/identity_manager/identity_test_utils.h new file mode 100644 index 00000000000..c7842e60214 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.h @@ -0,0 +1,187 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_TEST_UTILS_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_TEST_UTILS_H_ + +#include <string> + +#include "build/build_config.h" +#include "components/signin/public/identity_manager/account_info.h" + +namespace network { +class TestURLLoaderFactory; +} + +class GoogleServiceAuthError; + +// Test-related utilities that don't fit in either IdentityTestEnvironment or +// IdentityManager itself. NOTE: Using these utilities directly is discouraged, +// but sometimes necessary during conversion. Use IdentityTestEnvironment if +// possible. These utilities should be used directly only if the production code +// is using IdentityManager, but it is not yet feasible to convert the test code +// to use IdentityTestEnvironment. Any such usage should only be temporary, +// i.e., should be followed as quickly as possible by conversion of the test +// code to use IdentityTestEnvironment. +namespace signin { + +// Controls whether to keep or remove accounts when clearing the primary +// account. +enum class ClearPrimaryAccountPolicy { + // Use the default internal policy. + DEFAULT, + // Explicitly keep all accounts. + KEEP_ALL_ACCOUNTS, + // Explicitly remove all accounts. + REMOVE_ALL_ACCOUNTS +}; + +struct CookieParamsForTest { + std::string email; + std::string gaia_id; +}; + +class IdentityManager; + +// Sets the primary account (which must not already be set) to the given email +// address, generating a GAIA ID that corresponds uniquely to that email +// address. On non-ChromeOS, results in the firing of the IdentityManager and +// PrimaryAccountManager callbacks for signin success. Blocks until the primary +// account is set. Returns the CoreAccountInfo of the newly-set account. +// NOTE: See disclaimer at top of file re: direct usage. +CoreAccountInfo SetPrimaryAccount(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. +// NOTE: See disclaimer at top of file re: direct usage. +void SetRefreshTokenForPrimaryAccount( + IdentityManager* identity_manager, + const std::string& token_value = std::string()); + +// Sets a special invalid refresh token for the primary account (which must +// already be set). Blocks until the refresh token is set. +// NOTE: See disclaimer at top of file re: direct usage. +void SetInvalidRefreshTokenForPrimaryAccount(IdentityManager* identity_manager); + +// Removes any refresh token for the primary account, if present. Blocks until +// the refresh token is removed. +// NOTE: See disclaimer at top of file re: direct usage. +void RemoveRefreshTokenForPrimaryAccount(IdentityManager* identity_manager); + +// Makes the primary account (which must not already be set) available for the +// given email address, generating a GAIA ID and refresh token that correspond +// uniquely to that email address. On non-ChromeOS, results in the firing of the +// IdentityManager and PrimaryAccountManager callbacks for signin success. +// Blocks until the primary account is available. Returns the AccountInfo of the +// newly-available account. +// NOTE: See disclaimer at top of file re: direct usage. +AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager, + const std::string& email); + +// Clears the primary account if present, with |policy| used to determine +// whether to keep or remove all accounts. On non-ChromeOS, results in the +// firing of the IdentityManager and PrimaryAccountManager callbacks for +// signout. Blocks until the primary account is cleared. +// NOTE: See disclaimer at top of file re: direct usage. +void ClearPrimaryAccount( + IdentityManager* identity_manager, + ClearPrimaryAccountPolicy policy = ClearPrimaryAccountPolicy::DEFAULT); + +// Makes an account available for the given email address, generating a GAIA ID +// and refresh token that correspond uniquely to that email address. Blocks +// until the account is available. Returns the AccountInfo of the +// newly-available account. +// NOTE: See disclaimer at top of file re: direct usage. +AccountInfo MakeAccountAvailable(IdentityManager* identity_manager, + 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 to +// that email address. Blocks until the account is available. Returns the +// AccountInfo of the newly-available account. +// NOTE: See disclaimer at top of file re: direct usage. +AccountInfo MakeAccountAvailableWithCookies( + IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::string& email, + const std::string& gaia_id); + +// Sets a refresh token for the given account (which must already be available). +// Blocks until the refresh token is set. If |token_value| is empty a default +// value will be used instead. +// NOTE: See disclaimer at top of file re: direct usage. +void SetRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id, + const std::string& token_value = std::string()); + +// Sets a special invalid refresh token for the given account (which must +// already be available). Blocks until the refresh token is set. +// NOTE: See disclaimer at top of file re: direct usage. +void SetInvalidRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id); + +// Removes any refresh token that is present for the given account. Blocks until +// the refresh token is removed. Is a no-op if no refresh token is present for +// the given account. +// NOTE: See disclaimer at top of file re: direct usage. +void RemoveRefreshTokenForAccount(IdentityManager* identity_manager, + const std::string& account_id); + +// Puts the given accounts into the Gaia cookie, replacing any previous +// accounts. Blocks until the accounts have been set. +// |test_url_loader_factory| is used to set a fake ListAccounts response +// containing the provided |cookie_accounts|, which are then put into +// the Gaia cookie. +// NOTE: See disclaimer at top of file re: direct usage. +void SetCookieAccounts(IdentityManager* identity_manager, + network::TestURLLoaderFactory* test_url_loader_factory, + const std::vector<CookieParamsForTest>& cookie_accounts); + +// Updates the info for |account_info.account_id|, which must be a known +// account. +void UpdateAccountInfoForAccount(IdentityManager* identity_manager, + AccountInfo account_info); + +// Sets whether the list of accounts in Gaia cookie jar is fresh and does not +// need to be updated. +void SetFreshnessOfAccountsInGaiaCookie(IdentityManager* identity_manager, + bool accounts_are_fresh); + +std::string GetTestGaiaIdForEmail(const std::string& email); + +// Updates the persistent auth error set on |account_id| which must be a known +// account, i.e., an account with a refresh token. +void UpdatePersistentErrorOfRefreshTokenForAccount( + IdentityManager* identity_manager, + const std::string& account_id, + const GoogleServiceAuthError& auth_error); + +// Disables internal retries of failed access token fetches. +void DisableAccessTokenFetchRetries(IdentityManager* identity_manager); + +#if defined(OS_ANDROID) +// Disables interaction with system accounts, which requires special permission. +void DisableInteractionWithSystemAccounts(); +#endif + +// Cancels all ongoing operations related to the accounts in the Gaia cookie. +void CancelAllOngoingGaiaCookieOperations(IdentityManager* identity_manager); + +// Simulate account fetching using AccountTrackerService without sending +// network requests. +void SimulateSuccessfulFetchOfAccountInfo(IdentityManager* identity_manager, + const std::string& account_id, + const std::string& email, + const std::string& gaia, + const std::string& hosted_domain, + const std::string& full_name, + const std::string& given_name, + const std::string& locale, + const std::string& picture_url); +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_TEST_UTILS_H_ diff --git a/chromium/components/signin/core/browser/identity_utils.cc b/chromium/components/signin/public/identity_manager/identity_utils.cc index ed3cb950eac..11061a1d694 100644 --- a/chromium/components/signin/core/browser/identity_utils.cc +++ b/chromium/components/signin/public/identity_manager/identity_utils.cc @@ -2,16 +2,19 @@ // 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/identity_utils.h" +#include "components/signin/public/identity_manager/identity_utils.h" #include <string> #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/base/signin_pref_names.h" #include "third_party/icu/source/i18n/unicode/regex.h" -namespace identity { +namespace signin { + +namespace { bool IsUsernameAllowedByPattern(base::StringPiece username, base::StringPiece pattern) { @@ -47,18 +50,12 @@ bool IsUsernameAllowedByPattern(base::StringPiece username, return !!match; // !! == convert from UBool to bool. } -bool LegacyIsUsernameAllowedByPatternFromPrefs( - PrefService* prefs, - const std::string& username, - const std::string& pattern_pref_name) { - // TODO(crbug.com/908121): We need to deal for now with the fact that most - // unit tests don't register a local state with the browser process, in which - // case all usernames are considered 'allowed'. - if (!prefs) - return true; +} // namespace - return IsUsernameAllowedByPattern(username, - prefs->GetString(pattern_pref_name)); +bool IsUsernameAllowedByPatternFromPrefs(const PrefService* prefs, + const std::string& username) { + return IsUsernameAllowedByPattern( + username, prefs->GetString(prefs::kGoogleServicesUsernamePattern)); } -} // namespace identity +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/identity_utils.h b/chromium/components/signin/public/identity_manager/identity_utils.h new file mode 100644 index 00000000000..62927ab4e7e --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_utils.h @@ -0,0 +1,28 @@ +// 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" + +class PrefService; + +namespace signin { + +// Returns true if the username is allowed based on a pattern registered +// |prefs::kGoogleServicesUsernamePattern| with the preferences service +// referenced by |prefs|. +bool IsUsernameAllowedByPatternFromPrefs(const PrefService* prefs, + const std::string& username); +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IDENTITY_UTILS_H_ diff --git a/chromium/components/signin/public/identity_manager/identity_utils_unittest.cc b/chromium/components/signin/public/identity_manager/identity_utils_unittest.cc new file mode 100644 index 00000000000..0be69ddf6f4 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/identity_utils_unittest.cc @@ -0,0 +1,97 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/identity_utils.h" + +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/public/base/signin_pref_names.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kUsername[] = "test@test.com"; + +const char kValidWildcardPattern[] = ".*@test.com"; +const char kInvalidWildcardPattern[] = "*@test.com"; + +const char kMatchingPattern1[] = "test@test.com"; +const char kMatchingPattern2[] = ".*@test.com"; +const char kMatchingPattern3[] = "test@.*.com"; +const char kMatchingPattern4[] = ".*@.*.com"; +const char kMatchingPattern5[] = ".*@.*"; +const char kMatchingPattern6[] = ".*"; + +const char kNonMatchingPattern[] = ".*foo.*"; +const char kNonMatchingUsernamePattern[] = "foo@test.com"; +const char kNonMatchingDomainPattern[] = "test@foo.com"; +} // namespace + +class IdentityUtilsTest : public testing::Test { + public: + IdentityUtilsTest() { + prefs_.registry()->RegisterStringPref(prefs::kGoogleServicesUsernamePattern, + std::string()); + } + + TestingPrefServiceSimple* prefs() { return &prefs_; } + + private: + TestingPrefServiceSimple prefs_; +}; + +TEST_F(IdentityUtilsTest, IsUsernameAllowedByPatternFromPrefs_EmptyPatterns) { + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, ""); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, " "); + EXPECT_FALSE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); +} + +TEST_F(IdentityUtilsTest, + IsUsernameAllowedByPatternFromPrefs_InvalidWildcardPatterns) { + // signin::IsUsernameAllowedByPatternFromPrefs should recognize invalid + // wildcard patterns like "*@foo.com" and insert a "." before them + // automatically. + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, + kValidWildcardPattern); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, + kInvalidWildcardPattern); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); +} + +TEST_F(IdentityUtilsTest, + IsUsernameAllowedByPatternFromPrefs_MatchingWildcardPatterns) { + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern1); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern2); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern3); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern4); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern5); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, kMatchingPattern6); + EXPECT_TRUE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, + kNonMatchingPattern); + EXPECT_FALSE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, + kNonMatchingUsernamePattern); + EXPECT_FALSE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); + + prefs()->SetString(prefs::kGoogleServicesUsernamePattern, + kNonMatchingDomainPattern); + EXPECT_FALSE(signin::IsUsernameAllowedByPatternFromPrefs(prefs(), kUsername)); +} diff --git a/chromium/components/signin/public/identity_manager/ios/BUILD.gn b/chromium/components/signin/public/identity_manager/ios/BUILD.gn new file mode 100644 index 00000000000..0f06fb4a39b --- /dev/null +++ b/chromium/components/signin/public/identity_manager/ios/BUILD.gn @@ -0,0 +1,32 @@ +# 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. + +source_set("ios") { + configs += [ "//build/config/compiler:enable_arc" ] + + sources = [ + "device_accounts_provider.h", + "device_accounts_provider.mm", + ] + + public_deps = [ + "//base", + ] +} + +source_set("test_support") { + configs += [ "//build/config/compiler:enable_arc" ] + + testonly = true + + sources = [ + "fake_device_accounts_provider.h", + "fake_device_accounts_provider.mm", + ] + + public_deps = [ + ":ios", + "//base", + ] +} diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h b/chromium/components/signin/public/identity_manager/ios/device_accounts_provider.h index 2696f19c368..bbb5bbc87f8 100644 --- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h +++ b/chromium/components/signin/public/identity_manager/ios/device_accounts_provider.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_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_PROVIDER_H_ -#define COMPONENTS_SIGNIN_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_PROVIDER_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_DEVICE_ACCOUNTS_PROVIDER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_DEVICE_ACCOUNTS_PROVIDER_H_ #if defined(__OBJC__) @class NSDate; @@ -37,21 +37,22 @@ enum AuthenticationErrorCategory { kAuthenticationErrorCategoryUnknownIdentityErrors, }; -// Interface that provides support for ProfileOAuth2TokenServiceIOS. -class ProfileOAuth2TokenServiceIOSProvider { +// Interface that provides a mechanism for interacting with the underlying +// device accounts support. +class DeviceAccountsProvider { public: // Account information. struct AccountInfo { std::string gaia; std::string email; + std::string hosted_domain; }; - typedef base::Callback<void(NSString* token, - NSDate* expiration, - NSError* error)> AccessTokenCallback; + using AccessTokenCallback = base::OnceCallback< + void(NSString* token, NSDate* expiration, NSError* error)>; - ProfileOAuth2TokenServiceIOSProvider() {} - virtual ~ProfileOAuth2TokenServiceIOSProvider() {} + DeviceAccountsProvider() {} + virtual ~DeviceAccountsProvider() {} // Returns the ids of all accounts. virtual std::vector<AccountInfo> GetAllAccounts() const; @@ -61,7 +62,7 @@ class ProfileOAuth2TokenServiceIOSProvider { virtual void GetAccessToken(const std::string& gaia_id, const std::string& client_id, const std::set<std::string>& scopes, - const AccessTokenCallback& callback); + AccessTokenCallback callback); // Returns the authentication error category of |error| associated with the // account with id |gaia_id|. @@ -70,4 +71,4 @@ class ProfileOAuth2TokenServiceIOSProvider { NSError* error) const; }; -#endif // COMPONENTS_SIGNIN_IOS_BROWSER_PROFILE_OAUTH2_TOKEN_SERVICE_IOS_PROVIDER_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_DEVICE_ACCOUNTS_PROVIDER_H_ diff --git a/chromium/components/signin/public/identity_manager/ios/device_accounts_provider.mm b/chromium/components/signin/public/identity_manager/ios/device_accounts_provider.mm new file mode 100644 index 00000000000..9e11272a25a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/ios/device_accounts_provider.mm @@ -0,0 +1,26 @@ +// Copyright 2016 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/identity_manager/ios/device_accounts_provider.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +std::vector<DeviceAccountsProvider::AccountInfo> +DeviceAccountsProvider::GetAllAccounts() const { + return std::vector<DeviceAccountsProvider::AccountInfo>(); +} + +void DeviceAccountsProvider::GetAccessToken(const std::string& gaia_id, + const std::string& client_id, + const std::set<std::string>& scopes, + AccessTokenCallback callback) {} + +AuthenticationErrorCategory +DeviceAccountsProvider::GetAuthenticationErrorCategory( + const std::string& gaia_id, + NSError* error) const { + return kAuthenticationErrorCategoryUnknownErrors; +} diff --git a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h b/chromium/components/signin/public/identity_manager/ios/fake_device_accounts_provider.h index 9fabc729041..c4b442d0149 100644 --- a/chromium/components/signin/ios/browser/fake_profile_oauth2_token_service_ios_provider.h +++ b/chromium/components/signin/public/identity_manager/ios/fake_device_accounts_provider.h @@ -2,29 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef IOS_TEST_MOCK_PROFILE_OAUTH2_TOKEN_SERVICE_PROVIDER_IOS_H_ -#define IOS_TEST_MOCK_PROFILE_OAUTH2_TOKEN_SERVICE_PROVIDER_IOS_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_FAKE_DEVICE_ACCOUNTS_PROVIDER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_FAKE_DEVICE_ACCOUNTS_PROVIDER_H_ -#include <memory> +#include <set> #include <string> #include <utility> #include <vector> #include "base/macros.h" -#include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h" +#include "components/signin/public/identity_manager/ios/device_accounts_provider.h" -// Mock class of ProfileOAuth2TokenServiceIOSProvider for testing. -class FakeProfileOAuth2TokenServiceIOSProvider - : public ProfileOAuth2TokenServiceIOSProvider { +// Mock class of DeviceAccountsProvider for testing. +class FakeDeviceAccountsProvider : public DeviceAccountsProvider { public: - FakeProfileOAuth2TokenServiceIOSProvider(); - ~FakeProfileOAuth2TokenServiceIOSProvider() override; + FakeDeviceAccountsProvider(); + ~FakeDeviceAccountsProvider() override; - // ProfileOAuth2TokenServiceIOSProvider + // DeviceAccountsProvider void GetAccessToken(const std::string& account_id, const std::string& client_id, const std::set<std::string>& scopes, - const AccessTokenCallback& callback) override; + AccessTokenCallback callback) override; std::vector<AccountInfo> GetAllAccounts() const override; AuthenticationErrorCategory GetAuthenticationErrorCategory( const std::string& gaia_id, @@ -39,12 +38,12 @@ class FakeProfileOAuth2TokenServiceIOSProvider void IssueAccessTokenErrorForAllRequests(); private: - typedef std::pair<std::string, AccessTokenCallback> AccessTokenRequest; + using AccessTokenRequest = std::pair<std::string, AccessTokenCallback>; std::vector<AccountInfo> accounts_; std::vector<AccessTokenRequest> requests_; - DISALLOW_COPY_AND_ASSIGN(FakeProfileOAuth2TokenServiceIOSProvider); + DISALLOW_COPY_AND_ASSIGN(FakeDeviceAccountsProvider); }; -#endif // IOS_TEST_PROVIDER_CHROME_BROWSER_SIGNIN_MOCK_PROFILE_OAUTH2_TOKEN_SERVICE_PROVIDER_IOS_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_IOS_FAKE_DEVICE_ACCOUNTS_PROVIDER_H_ diff --git a/chromium/components/signin/public/identity_manager/ios/fake_device_accounts_provider.mm b/chromium/components/signin/public/identity_manager/ios/fake_device_accounts_provider.mm new file mode 100644 index 00000000000..6c65e6d038a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/ios/fake_device_accounts_provider.mm @@ -0,0 +1,73 @@ +// 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. + +#include "components/signin/public/identity_manager/ios/fake_device_accounts_provider.h" + +#import <Foundation/Foundation.h> + +#include "base/logging.h" +#include "base/strings/sys_string_conversions.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +FakeDeviceAccountsProvider::FakeDeviceAccountsProvider() {} + +FakeDeviceAccountsProvider::~FakeDeviceAccountsProvider() {} + +void FakeDeviceAccountsProvider::GetAccessToken( + const std::string& account_id, + const std::string& client_id, + const std::set<std::string>& scopes, + AccessTokenCallback callback) { + requests_.push_back(AccessTokenRequest(account_id, std::move(callback))); +} + +std::vector<DeviceAccountsProvider::AccountInfo> +FakeDeviceAccountsProvider::GetAllAccounts() const { + return accounts_; +} + +DeviceAccountsProvider::AccountInfo FakeDeviceAccountsProvider::AddAccount( + const std::string& gaia, + const std::string& email) { + DeviceAccountsProvider::AccountInfo account; + account.gaia = gaia; + account.email = email; + accounts_.push_back(account); + return account; +} + +void FakeDeviceAccountsProvider::ClearAccounts() { + accounts_.clear(); +} + +void FakeDeviceAccountsProvider::IssueAccessTokenForAllRequests() { + for (auto& pair : requests_) { + NSString* access_token = [NSString + stringWithFormat:@"fake_access_token [account=%s]", pair.first.c_str()]; + NSDate* one_hour_from_now = [NSDate dateWithTimeIntervalSinceNow:3600]; + std::move(pair.second).Run(access_token, one_hour_from_now, nil); + } + requests_.clear(); +} + +void FakeDeviceAccountsProvider::IssueAccessTokenErrorForAllRequests() { + for (auto& pair : requests_) { + NSError* error = [[NSError alloc] initWithDomain:@"fake_access_token_error" + code:-1 + userInfo:nil]; + std::move(pair.second).Run(nil, nil, error); + } + requests_.clear(); +} + +AuthenticationErrorCategory +FakeDeviceAccountsProvider::GetAuthenticationErrorCategory( + const std::string& gaia_id, + NSError* error) const { + DCHECK(error); + return kAuthenticationErrorCategoryAuthorizationErrors; +} diff --git a/chromium/components/signin/public/identity_manager/load_credentials_state.h b/chromium/components/signin/public/identity_manager/load_credentials_state.h new file mode 100644 index 00000000000..cd85058683d --- /dev/null +++ b/chromium/components/signin/public/identity_manager/load_credentials_state.h @@ -0,0 +1,25 @@ +// 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_PUBLIC_IDENTITY_MANAGER_LOAD_CREDENTIALS_STATE_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_LOAD_CREDENTIALS_STATE_H_ + +namespace signin { + +enum class LoadCredentialsState { + // Describes the status of the operation of loading OAuth2 refresh tokens from + // disk. + LOAD_CREDENTIALS_NOT_STARTED, + LOAD_CREDENTIALS_IN_PROGRESS, + LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED, + LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS, + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS, + LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT, + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS, +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_LOAD_CREDENTIALS_STATE_H_ diff --git a/chromium/components/signin/public/identity_manager/objc/BUILD.gn b/chromium/components/signin/public/identity_manager/objc/BUILD.gn new file mode 100644 index 00000000000..ba323fe7dae --- /dev/null +++ b/chromium/components/signin/public/identity_manager/objc/BUILD.gn @@ -0,0 +1,15 @@ +# 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. + +source_set("objc") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "identity_manager_observer_bridge.h", + "identity_manager_observer_bridge.mm", + ] + + public_deps = [ + "//components/signin/public/identity_manager", + ] +} diff --git a/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h new file mode 100644 index 00000000000..3101764cf00 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_OBJC_IDENTITY_MANAGER_OBSERVER_BRIDGE_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_OBJC_IDENTITY_MANAGER_OBSERVER_BRIDGE_H_ + +#import <Foundation/Foundation.h> +#include <vector> + +#include "components/signin/public/identity_manager/identity_manager.h" + +// Implement this protocol and pass your implementation into an +// IdentityManagerObserverBridge object to receive IdentityManager observer +// callbacks in Objective-C. +@protocol IdentityManagerObserverBridgeDelegate <NSObject> + +@optional + +// These callbacks follow the semantics of the corresponding +// IdentityManager::Observer callbacks. See the comments on +// IdentityManager::Observer in identity_manager.h for the specification of +// these semantics. + +- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo; +- (void)onPrimaryAccountCleared: + (const CoreAccountInfo&)previousPrimaryAccountInfo; +- (void)onRefreshTokenUpdatedForAccount:(const CoreAccountInfo&)accountInfo; +- (void)onRefreshTokenRemovedForAccount:(const std::string&)accountId; +- (void)onRefreshTokensLoaded; +- (void)onAccountsInCookieUpdated: + (const signin::AccountsInCookieJarInfo&)accountsInCookieJarInfo + error:(const GoogleServiceAuthError&)error; +- (void)onEndBatchOfRefreshTokenStateChanges; + +@end + +namespace signin { + +// Bridge class that listens for |IdentityManager| notifications and +// passes them to its Objective-C delegate. +class IdentityManagerObserverBridge : public IdentityManager::Observer { + public: + IdentityManagerObserverBridge( + IdentityManager* identity_manager, + id<IdentityManagerObserverBridgeDelegate> delegate); + ~IdentityManagerObserverBridge() override; + + // IdentityManager::Observer. + void OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) override; + void OnPrimaryAccountCleared( + const CoreAccountInfo& previous_primary_account_info) override; + void OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) override; + void OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) override; + void OnRefreshTokensLoaded() override; + void OnAccountsInCookieUpdated( + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const GoogleServiceAuthError& error) override; + void OnEndBatchOfRefreshTokenStateChanges() override; + + private: + // Identity manager to observe. + IdentityManager* identity_manager_; + // Delegate to call. + __weak id<IdentityManagerObserverBridgeDelegate> delegate_; + + DISALLOW_COPY_AND_ASSIGN(IdentityManagerObserverBridge); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_OBJC_IDENTITY_MANAGER_OBSERVER_BRIDGE_H_ diff --git a/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.mm b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.mm new file mode 100644 index 00000000000..4826a43ca5a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.mm @@ -0,0 +1,77 @@ +// 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. + +#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace signin { + +IdentityManagerObserverBridge::IdentityManagerObserverBridge( + IdentityManager* identity_manager, + id<IdentityManagerObserverBridgeDelegate> delegate) + : identity_manager_(identity_manager), delegate_(delegate) { + identity_manager_->AddObserver(this); +} + +IdentityManagerObserverBridge::~IdentityManagerObserverBridge() { + identity_manager_->RemoveObserver(this); +} + +void IdentityManagerObserverBridge::OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) { + if ([delegate_ respondsToSelector:@selector(onPrimaryAccountSet:)]) { + [delegate_ onPrimaryAccountSet:primary_account_info]; + } +} + +void IdentityManagerObserverBridge::OnPrimaryAccountCleared( + const CoreAccountInfo& previous_primary_account_info) { + if ([delegate_ respondsToSelector:@selector(onPrimaryAccountCleared:)]) { + [delegate_ onPrimaryAccountCleared:previous_primary_account_info]; + } +} + +void IdentityManagerObserverBridge::OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) { + if ([delegate_ + respondsToSelector:@selector(onRefreshTokenUpdatedForAccount:)]) { + [delegate_ onRefreshTokenUpdatedForAccount:account_info]; + } +} + +void IdentityManagerObserverBridge::OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) { + if ([delegate_ + respondsToSelector:@selector(onRefreshTokenRemovedForAccount:)]) { + [delegate_ onRefreshTokenRemovedForAccount:account_id.id]; + } +} + +void IdentityManagerObserverBridge::OnRefreshTokensLoaded() { + if ([delegate_ respondsToSelector:@selector(onRefreshTokensLoaded)]) { + [delegate_ onRefreshTokensLoaded]; + } +} + +void IdentityManagerObserverBridge::OnAccountsInCookieUpdated( + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const GoogleServiceAuthError& error) { + if ([delegate_ respondsToSelector:@selector(onAccountsInCookieUpdated: + error:)]) { + [delegate_ onAccountsInCookieUpdated:accounts_in_cookie_jar_info + error:error]; + } +} + +void IdentityManagerObserverBridge::OnEndBatchOfRefreshTokenStateChanges() { + if ([delegate_ + respondsToSelector:@selector(onEndBatchOfRefreshTokenStateChanges)]) { + [delegate_ onEndBatchOfRefreshTokenStateChanges]; + } +} + +} // namespace signin 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 new file mode 100644 index 00000000000..5a1415fa58a --- /dev/null +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc @@ -0,0 +1,125 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h" + +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "components/signin/public/identity_manager/access_token_info.h" +#include "google_apis/gaia/google_service_auth_error.h" + +namespace signin { + +PrimaryAccountAccessTokenFetcher::PrimaryAccountAccessTokenFetcher( + const std::string& oauth_consumer_name, + IdentityManager* identity_manager, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + Mode mode) + : oauth_consumer_name_(oauth_consumer_name), + identity_manager_(identity_manager), + scopes_(scopes), + callback_(std::move(callback)), + identity_manager_observer_(this), + access_token_retried_(false), + mode_(mode) { + if (mode_ == Mode::kImmediate || AreCredentialsAvailable()) { + StartAccessTokenRequest(); + return; + } + + // Start observing the IdentityManager. This observer will be removed either + // when credentials are obtained and an access token request is started or + // when this object is destroyed. + identity_manager_observer_.Add(identity_manager_); +} + +PrimaryAccountAccessTokenFetcher::~PrimaryAccountAccessTokenFetcher() {} + +bool PrimaryAccountAccessTokenFetcher::AreCredentialsAvailable() const { + DCHECK_EQ(Mode::kWaitUntilAvailable, mode_); + + return identity_manager_->HasPrimaryAccountWithRefreshToken(); +} + +void PrimaryAccountAccessTokenFetcher::StartAccessTokenRequest() { + DCHECK(mode_ == Mode::kImmediate || AreCredentialsAvailable()); + + // By the time of starting an access token request, we should no longer be + // listening for signin-related events. + DCHECK(!identity_manager_observer_.IsObserving(identity_manager_)); + + // Note: We might get here even in cases where we know that there's no refresh + // token. We're requesting an access token anyway, so that the token service + // will generate an appropriate error code that we can return to the client. + DCHECK(!access_token_fetcher_); + + // NOTE: This class does not utilize AccessTokenFetcher in its + // |kWaitUntilRefreshTokenAvailable| mode because the PAATF semantics specify + // that when used in *its* |kWaitUntilAvailable| mode, the access token + // request should be started when the account is primary AND has a refresh + // 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_, + base::BindOnce( + &PrimaryAccountAccessTokenFetcher::OnAccessTokenFetchComplete, + base::Unretained(this)), + AccessTokenFetcher::Mode::kImmediate); +} + +void PrimaryAccountAccessTokenFetcher::OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) { + ProcessSigninStateChange(); +} + +void PrimaryAccountAccessTokenFetcher::OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) { + ProcessSigninStateChange(); +} + +void PrimaryAccountAccessTokenFetcher::ProcessSigninStateChange() { + DCHECK_EQ(Mode::kWaitUntilAvailable, mode_); + + if (!AreCredentialsAvailable()) + return; + + identity_manager_observer_.Remove(identity_manager_); + + StartAccessTokenRequest(); +} + +void PrimaryAccountAccessTokenFetcher::OnAccessTokenFetchComplete( + GoogleServiceAuthError error, + AccessTokenInfo access_token_info) { + access_token_fetcher_.reset(); + + // There is a special case for Android that RefreshTokenIsAvailable and + // StartRequest are called to pre-fetch the account image and name before + // sign-in. In that case, our ongoing access token request gets cancelled. + // Moreover, OnRefreshTokenAvailable might happen after startup when the + // credentials are changed/updated. + // To handle these cases, we retry a canceled request once. + // However, a request may also get cancelled for legitimate reasons, e.g. + // because the user signed out. In those cases, there's no point in retrying, + // so only retry if there (still) is a valid refresh token. + // NOTE: Maybe we should retry for all transient errors here, so that clients + // don't have to. + if (mode_ == Mode::kWaitUntilAvailable && !access_token_retried_ && + error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED && + AreCredentialsAvailable()) { + access_token_retried_ = true; + StartAccessTokenRequest(); + return; + } + + // Per the contract of this class, it is allowed for consumers to delete this + // object from within the callback that is run below. Hence, it is not safe to + // add any code below this call. + std::move(callback_).Run(std::move(error), std::move(access_token_info)); +} + +} // namespace signin 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 new file mode 100644 index 00000000000..68436bc1958 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h @@ -0,0 +1,105 @@ +// 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_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_ + +#include <memory> +#include <string> + +#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/identity_manager.h" +#include "services/identity/public/cpp/scope_set.h" + +class GoogleServiceAuthError; + +namespace signin { +struct AccessTokenInfo; + +// Helper class to ease the task of obtaining an OAuth2 access token for the +// authenticated account. This handles various special cases, e.g. when the +// refresh token isn't loaded yet (during startup), or when there is some +// transient error. +// May only be used on the UI thread. +class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { + public: + // Specifies how this instance should behave: + // |kImmediate|: Makes one-shot immediate request. + // |kWaitUntilAvailable|: Waits for the primary account to be available + // before making the request. In particular, "available" is defined as the + // moment when (a) there is a primary account and (b) that account has a + // refresh token. This semantics is richer than using an AccessTokenFetcher in + // kWaitUntilRefreshTokenAvailable mode, as the latter will make a request + // once the specified account has a refresh token, regardless of whether it's + // the primary account at that point. + // Note that using |kWaitUntilAvailable| can result in waiting forever + // if the user is not signed in and doesn't sign in. + enum class Mode { kImmediate, kWaitUntilAvailable }; + + // Instantiates a fetcher and immediately starts the process of obtaining an + // OAuth2 access token for the given |scopes|. The |callback| is called once + // the request completes (successful or not). If the + // PrimaryAccountAccessTokenFetcher is destroyed before the process completes, + // the callback is not called. + PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name, + IdentityManager* identity_manager, + const identity::ScopeSet& scopes, + AccessTokenFetcher::TokenCallback callback, + Mode mode); + + ~PrimaryAccountAccessTokenFetcher() override; + + // Exposed for tests. + bool access_token_request_retried() { return access_token_retried_; } + + private: + // Returns true iff there is a primary account with a refresh token. Should + // only be called in mode |kWaitUntilAvailable|. + bool AreCredentialsAvailable() const; + + void StartAccessTokenRequest(); + + // IdentityManager::Observer implementation. + void OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) override; + void OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) override; + + // Checks whether credentials are now available and starts an access token + // request if so. Should only be called in mode |kWaitUntilAvailable|. + void ProcessSigninStateChange(); + + // Invoked by |fetcher_| when an access token request completes. + void OnAccessTokenFetchComplete(GoogleServiceAuthError error, + AccessTokenInfo access_token_info); + + std::string oauth_consumer_name_; + IdentityManager* identity_manager_; + identity::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 + // assume that it is dead after invoking |callback_| and must not run any more + // code. + AccessTokenFetcher::TokenCallback callback_; + + ScopedObserver<IdentityManager, PrimaryAccountAccessTokenFetcher> + identity_manager_observer_; + + // Internal fetcher that does the actual access token request. + std::unique_ptr<AccessTokenFetcher> access_token_fetcher_; + + // When a token request gets canceled, we want to retry once. + bool access_token_retried_; + + Mode mode_; + + DISALLOW_COPY_AND_ASSIGN(PrimaryAccountAccessTokenFetcher); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_ 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 new file mode 100644 index 00000000000..b0136c69c2b --- /dev/null +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher_unittest.cc @@ -0,0 +1,401 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h" + +#include <utility> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/public/identity_manager/access_token_info.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gmock_mutant.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::MockCallback; +using testing::CallbackToFunctor; +using testing::InvokeWithoutArgs; +using testing::StrictMock; + +namespace signin { + +namespace { + +void OnAccessTokenFetchComplete( + base::OnceClosure done_closure, + const GoogleServiceAuthError& expected_error, + const AccessTokenInfo& expected_access_token_info, + GoogleServiceAuthError error, + AccessTokenInfo access_token_info) { + EXPECT_EQ(expected_error, error); + if (expected_error == GoogleServiceAuthError::AuthErrorNone()) + EXPECT_EQ(expected_access_token_info, access_token_info); + + std::move(done_closure).Run(); +} + +} // namespace + +class PrimaryAccountAccessTokenFetcherTest : public testing::Test, + public IdentityManager::Observer { + public: + using TestTokenCallback = + StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>; + + PrimaryAccountAccessTokenFetcherTest() + : access_token_info_("access token", + base::Time::Now() + base::TimeDelta::FromHours(1), + "id_token") {} + + ~PrimaryAccountAccessTokenFetcherTest() override {} + + std::unique_ptr<PrimaryAccountAccessTokenFetcher> CreateFetcher( + AccessTokenFetcher::TokenCallback callback, + PrimaryAccountAccessTokenFetcher::Mode mode) { + std::set<std::string> scopes{"scope"}; + return std::make_unique<PrimaryAccountAccessTokenFetcher>( + "test_consumer", identity_test_env_.identity_manager(), scopes, + std::move(callback), mode); + } + + IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; } + + // Signs the user in to the primary account, returning the account ID. + std::string SignIn() { + return identity_test_env_.MakePrimaryAccountAvailable("me@gmail.com") + .account_id; + } + + // Returns an AccessTokenInfo with valid information that can be used for + // completing access token requests. + const AccessTokenInfo& access_token_info() const { + return access_token_info_; + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + IdentityTestEnvironment identity_test_env_; + AccessTokenInfo access_token_info_; +}; + +TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotShouldReturnAccessToken) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), PrimaryAccountAccessTokenFetcher::Mode::kImmediate); + + // 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); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + WaitAndRetryShouldReturnAccessToken) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // 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); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), PrimaryAccountAccessTokenFetcher::Mode::kImmediate); + + // Destroy the fetcher before the access token request is fulfilled. + fetcher.reset(); + + // Fulfilling the request now should have no effect. + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + access_token_info().token, access_token_info().expiration_time, + access_token_info().id_token); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, OneShotCallsBackWhenSignedOut) { + base::RunLoop run_loop; + + // Signed out -> we should get called back. + auto fetcher = CreateFetcher( + base::BindOnce(&OnAccessTokenFetchComplete, run_loop.QuitClosure(), + GoogleServiceAuthError( + GoogleServiceAuthError::State::USER_NOT_SIGNED_UP), + AccessTokenInfo()), + PrimaryAccountAccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + OneShotCallsBackWhenNoRefreshToken) { + base::RunLoop run_loop; + + identity_test_env()->SetPrimaryAccount("me@gmail.com"); + + // Signed in, but there is no refresh token -> we should get called back. + auto fetcher = CreateFetcher( + base::BindOnce(&OnAccessTokenFetchComplete, run_loop.QuitClosure(), + GoogleServiceAuthError( + GoogleServiceAuthError::State::USER_NOT_SIGNED_UP), + AccessTokenInfo()), + PrimaryAccountAccessTokenFetcher::Mode::kImmediate); + + run_loop.Run(); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + WaitAndRetryNoCallbackWhenSignedOut) { + TestTokenCallback callback; + + // Signed out -> the fetcher should wait for a sign-in which never happens + // in this test, so we shouldn't get called back. + auto fetcher = CreateFetcher( + callback.Get(), + 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) { + TestTokenCallback callback; + + // Not signed in, so this should wait for a sign-in to complete. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + std::string account_id = SignIn(); + + // 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); + + // The request should not have to have been retried. + EXPECT_FALSE(fetcher->access_token_request_retried()); +} + +#endif // !OS_CHROMEOS + +TEST_F(PrimaryAccountAccessTokenFetcherTest, ShouldWaitForRefreshToken) { + TestTokenCallback callback; + + std::string account_id = + identity_test_env()->SetPrimaryAccount("me@gmail.com").account_id; + + // Signed in, but there is no refresh token -> we should not get called back + // (yet). + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // Getting a refresh token should result in a request for an access token. + identity_test_env()->SetRefreshTokenForPrimaryAccount(); + + // 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); + + // The request should not have to have been retried. + EXPECT_FALSE(fetcher->access_token_request_retried()); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldIgnoreRefreshTokensForOtherAccounts) { + TestTokenCallback callback; + + // Signed-in to account_id, but there's only a refresh token for a different + // account. + std::string account_id = + identity_test_env()->SetPrimaryAccount("me@gmail.com").account_id; + identity_test_env()->MakeAccountAvailable(account_id + "2"); + + // The fetcher should wait for the correct refresh token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // A refresh token for yet another account shouldn't matter either. + identity_test_env()->MakeAccountAvailable(account_id + "3"); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + OneShotCanceledAccessTokenRequest) { + std::string account_id = SignIn(); + + base::RunLoop run_loop; + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + base::BindOnce( + &OnAccessTokenFetchComplete, run_loop.QuitClosure(), + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo()), + PrimaryAccountAccessTokenFetcher::Mode::kImmediate); + + // A canceled access token request should result in a callback. + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + WaitAndRetryCanceledAccessTokenRequest) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // A canceled access token request should get retried once. + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + // 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); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldRetryCanceledAccessTokenRequestOnlyOnce) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // A canceled access token request should get retried once. + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + // On the second failure, we should get called back with an empty access + // token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); +} + +#if !defined(OS_CHROMEOS) + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldNotRetryCanceledAccessTokenRequestIfSignedOut) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // Simulate the user signing out while the access token request is pending. + // In this case, the pending request gets canceled, and the fetcher should + // *not* retry. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())); + + identity_test_env()->ClearPrimaryAccount(); +} + +#endif // !OS_CHROMEOS + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldNotRetryCanceledAccessTokenRequestIfRefreshTokenRevoked) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // Simulate the refresh token getting removed. In this case, pending + // access token requests get canceled, and the fetcher should *not* retry. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + AccessTokenInfo())); + identity_test_env()->RemoveRefreshTokenForPrimaryAccount(); +} + +TEST_F(PrimaryAccountAccessTokenFetcherTest, + ShouldNotRetryFailedAccessTokenRequest) { + TestTokenCallback callback; + + std::string account_id = SignIn(); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher( + callback.Get(), + PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); + + // An access token failure other than "canceled" should not be retried; we + // should immediately get called back with an empty access token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), + AccessTokenInfo())); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( + GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); +} + +} // 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 new file mode 100644 index 00000000000..94755efd2cd --- /dev/null +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator.h @@ -0,0 +1,87 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_H_ + +#include <string> + +namespace signin_metrics { +enum ProfileSignout : int; +enum class SignoutDelete; +} // namespace signin_metrics + +struct CoreAccountId; + +namespace signin { + +// PrimaryAccountMutator is the interface to set and clear the primary account +// (see IdentityManager for more information). +// +// It is a pure interface that has concrete implementation on platform that +// support changing the signed-in state during the lifetime of the application. +// On other platforms, there is no implementation, and no instance will be +// available at runtime (thus accessors may return null). +class PrimaryAccountMutator { + public: + // Represents the options for handling the accounts known to the + // IdentityManager upon calling ClearPrimaryAccount(). + enum class ClearAccountsAction { + kDefault, // Default action based on internal policy. + kKeepAll, // Keep all accounts. + kRemoveAll, // Remove all accounts. + }; + + PrimaryAccountMutator() = default; + virtual ~PrimaryAccountMutator() = default; + + // PrimaryAccountMutator is non-copyable, non-moveable. + PrimaryAccountMutator(PrimaryAccountMutator&& other) = delete; + PrimaryAccountMutator const& operator=(PrimaryAccountMutator&& other) = + delete; + + PrimaryAccountMutator(const PrimaryAccountMutator& other) = delete; + PrimaryAccountMutator const& operator=(const PrimaryAccountMutator& other) = + delete; + + // Marks the account with |account_id| as the primary account, and returns + // whether the operation succeeded or not. To succeed, this requires that: + // - the account is known by the IdentityManager. + // On non-ChromeOS platforms, this additionally requires that: + // - setting the primary account is allowed, + // - the account username is allowed by policy, + // - there is not already a primary account set. + // TODO(https://crbug.com/983124): Investigate adding all the extra + // requirements on ChromeOS as well. + virtual bool SetPrimaryAccount(const CoreAccountId& account_id) = 0; + +#if defined(OS_CHROMEOS) + // 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. + // NOTE: Unlike SetPrimaryAccount(), this method does not require that the + // account is known by IdentityManager. The reason is that there are + // contexts on ChromeOS where the primary account is not guaranteed to be + // known by IdentityManager when it is set. + // TODO(https://crbug.com/967605): Port callers to SetPrimaryAccount() once + // https://crbug.com/867602 is fixed. + virtual bool SetPrimaryAccountAndUpdateAccountInfo( + const std::string& gaia_id, + const std::string& email) = 0; +#endif + +#if !defined(OS_CHROMEOS) + // Clears the primary account, and returns whether the operation + // succeeded or not. Depending on |action|, the other accounts + // known to the IdentityManager may be deleted. + virtual bool ClearPrimaryAccount( + ClearAccountsAction action, + signin_metrics::ProfileSignout source_metric, + signin_metrics::SignoutDelete delete_metric) = 0; +#endif +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_H_ 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 new file mode 100644 index 00000000000..a18d03c290f --- /dev/null +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc @@ -0,0 +1,499 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/public/identity_manager/primary_account_mutator.h" + +#include "base/bind.h" +#include "base/containers/flat_set.h" +#include "base/run_loop.h" +#include "base/scoped_observer.h" +#include "base/test/scoped_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/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" + +namespace { + +// Constants used by the different tests. +const char kPrimaryAccountEmail[] = "primary.account@example.com"; +#if !defined(OS_CHROMEOS) +const char kAnotherAccountEmail[] = "another.account@example.com"; +const char kUnknownAccountId[] = "{unknown account id}"; + +// All account consistency methods that are tested by those unit tests when +// testing ClearPrimaryAccount method. +const signin::AccountConsistencyMethod kTestedAccountConsistencyMethods[] = { + signin::AccountConsistencyMethod::kDisabled, + signin::AccountConsistencyMethod::kMirror, + signin::AccountConsistencyMethod::kDiceMigration, + signin::AccountConsistencyMethod::kDice, +}; + +// See RunClearPrimaryAccountTest(). +enum class AuthExpectation { kAuthNormal, kAuthError }; +enum class RemoveAccountExpectation { kKeepAll, kRemovePrimary, kRemoveAll }; + +// This callback will be invoked every time the IdentityManager::Observer +// method OnPrimaryAccountCleared is invoked. The parameter will be a +// reference to the still valid primary account that was cleared. +using PrimaryAccountClearedCallback = + base::RepeatingCallback<void(const CoreAccountInfo&)>; + +// This callback will be invoked every time the IdentityManager::Observer +// method OnRefreshTokenRemoved is invoked. The parameter will be a reference +// to the account_id whose token was removed. +using RefreshTokenRemovedCallback = + base::RepeatingCallback<void(const std::string&)>; + +// Helper IdentityManager::Observer that forwards some events to the +// callback passed to the constructor. +class ClearPrimaryAccountTestObserver + : public signin::IdentityManager::Observer { + public: + ClearPrimaryAccountTestObserver( + signin::IdentityManager* identity_manager, + PrimaryAccountClearedCallback on_primary_account_cleared, + RefreshTokenRemovedCallback on_refresh_token_removed) + : on_primary_account_cleared_(std::move(on_primary_account_cleared)), + on_refresh_token_removed_(std::move(on_refresh_token_removed)), + scoped_observer_(this) { + DCHECK(on_primary_account_cleared_); + DCHECK(on_refresh_token_removed_); + scoped_observer_.Add(identity_manager); + } + + // signin::IdentityManager::Observer implementation. + void OnPrimaryAccountCleared(const CoreAccountInfo& account_info) override { + on_primary_account_cleared_.Run(account_info); + } + + void OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) override { + on_refresh_token_removed_.Run(account_id); + } + + private: + PrimaryAccountClearedCallback on_primary_account_cleared_; + RefreshTokenRemovedCallback on_refresh_token_removed_; + ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer> + scoped_observer_; + + DISALLOW_COPY_AND_ASSIGN(ClearPrimaryAccountTestObserver); +}; + +// Helper for testing of ClearPrimaryAccount(). This function requires lots +// of tests due to having different behaviors based on its arguments. But the +// setup and execution of these test is all the boiler plate you see here: +// 1) Ensure you have 2 accounts, both with refresh tokens +// 2) Clear the primary account +// 3) Assert clearing succeeds and refresh tokens are optionally removed based +// on arguments. +// +// Optionally, it's possible to specify whether a normal auth process will +// take place, or whether an auth error should happen, useful for some tests. +void RunClearPrimaryAccountTest( + signin::AccountConsistencyMethod account_consistency_method, + signin::PrimaryAccountMutator::ClearAccountsAction account_action, + RemoveAccountExpectation account_expectation, + AuthExpectation auth_expection = AuthExpectation::kAuthNormal) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment( + /*test_url_loader_factory=*/nullptr, /*pref_service=*/nullptr, + account_consistency_method); + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + // With the exception of ClearPrimaryAccount_AuthInProgress, every other + // ClearPrimaryAccount_* test requires a primary account to be signed in. + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + AccountInfo account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + EXPECT_TRUE( + primary_account_mutator->SetPrimaryAccount(account_info.account_id)); + EXPECT_TRUE(identity_manager->HasPrimaryAccount()); + EXPECT_TRUE(identity_manager->HasPrimaryAccountWithRefreshToken()); + + EXPECT_EQ(identity_manager->GetPrimaryAccountId(), account_info.account_id); + EXPECT_EQ(identity_manager->GetPrimaryAccountInfo().email, + kPrimaryAccountEmail); + + if (auth_expection == AuthExpectation::kAuthError) { + // Set primary account to have authentication error. + SetRefreshTokenForPrimaryAccount(identity_manager); + signin::UpdatePersistentErrorOfRefreshTokenForAccount( + identity_manager, account_info.account_id, + GoogleServiceAuthError( + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS)); + } + + // Additionally, ClearPrimaryAccount_* tests also need a secondary account. + AccountInfo secondary_account_info = + environment.MakeAccountAvailable(kAnotherAccountEmail); + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + secondary_account_info.account_id)); + + // Grab this before clearing for token checks below. + auto former_primary_account = identity_manager->GetPrimaryAccountInfo(); + + // Make sure we exit the run loop. + base::RunLoop run_loop; + PrimaryAccountClearedCallback primary_account_cleared_callback = + base::BindRepeating([](base::RepeatingClosure quit_closure, + const CoreAccountInfo&) { quit_closure.Run(); }, + run_loop.QuitClosure()); + + // Track Observer token removal notification. + base::flat_set<std::string> observed_removals; + RefreshTokenRemovedCallback refresh_token_removed_callback = + base::BindRepeating( + [](base::flat_set<std::string>* observed_removals, + const std::string& removed_account) { + observed_removals->insert(removed_account); + }, + &observed_removals); + + ClearPrimaryAccountTestObserver scoped_observer( + identity_manager, primary_account_cleared_callback, + refresh_token_removed_callback); + + primary_account_mutator->ClearPrimaryAccount( + account_action, signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC); + run_loop.Run(); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + // NOTE: IdentityManager _may_ still possess this token (see switch below), + // but it is no longer considered part of the primary account. + EXPECT_FALSE(identity_manager->HasPrimaryAccountWithRefreshToken()); + + switch (account_expectation) { + case RemoveAccountExpectation::kKeepAll: + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + former_primary_account.account_id)); + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + secondary_account_info.account_id)); + EXPECT_TRUE(observed_removals.empty()); + break; + case RemoveAccountExpectation::kRemovePrimary: + EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( + former_primary_account.account_id)); + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + secondary_account_info.account_id)); + EXPECT_TRUE(base::Contains(observed_removals, + former_primary_account.account_id.id)); + EXPECT_FALSE(base::Contains(observed_removals, + secondary_account_info.account_id.id)); + break; + case RemoveAccountExpectation::kRemoveAll: + EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( + former_primary_account.account_id)); + EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( + secondary_account_info.account_id)); + EXPECT_TRUE(base::Contains(observed_removals, + former_primary_account.account_id.id)); + EXPECT_TRUE(base::Contains(observed_removals, + secondary_account_info.account_id.id)); + break; + } +} +#endif // !defined(OS_CHROMEOS) + +} // namespace + +using PrimaryAccountMutatorTest = PlatformTest; + +// Checks that setting the primary account works. +TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + AccountInfo account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + + EXPECT_FALSE(environment.identity_manager()->HasPrimaryAccount()); + EXPECT_TRUE( + primary_account_mutator->SetPrimaryAccount(account_info.account_id)); + + EXPECT_TRUE(identity_manager->HasPrimaryAccount()); + EXPECT_EQ(identity_manager->GetPrimaryAccountId(), account_info.account_id); +} + +// Tests that various preconditions of SetPrimaryAccount() not being satisfied +// should cause the setting of the primary account to fail. Not run on +// ChromeOS, where those preconditions do not exist. +// TODO(https://crbug.com/983124): Run these tests on ChromeOS if/once we +// 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. +TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_NoAccount) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount(kUnknownAccountId)); +} + +// Checks that setting the primary account fails if the account is unknown. +TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_UnknownAccount) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + AccountInfo account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount(kUnknownAccountId)); +} + +// Checks that trying to set the primary account fails when there is already a +// primary account. +TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_AlreadyHasPrimaryAccount) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + AccountInfo primary_account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + AccountInfo another_account_info = + environment.MakeAccountAvailable(kAnotherAccountEmail); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_TRUE(primary_account_mutator->SetPrimaryAccount( + primary_account_info.account_id)); + + EXPECT_TRUE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( + another_account_info.account_id)); + + EXPECT_EQ(identity_manager->GetPrimaryAccountId(), + primary_account_info.account_id); +} + +// Checks that trying to set the primary account fails if setting the primary +// account is not allowed. +TEST_F(PrimaryAccountMutatorTest, + SetPrimaryAccount_SettingPrimaryAccountForbidden) { + base::test::ScopedTaskEnvironment task_environment; + + sync_preferences::TestingPrefServiceSyncable pref_service; + signin::IdentityTestEnvironment environment( + /*test_url_loader_factory=*/nullptr, &pref_service); + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + AccountInfo primary_account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + + // Configure prefs so that setting the primary account is disallowed. + pref_service.SetBoolean(prefs::kSigninAllowed, false); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( + primary_account_info.account_id)); +} +#endif // !defined(OS_CHROMEOS) + +// End of tests of preconditions not being satisfied causing the setting of +// the primary account to fail. + +// Tests of clearing the primary account. Not run on ChromeOS, which does not +// support clearing the primary account. +#if !defined(OS_CHROMEOS) +TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_NotSignedIn) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + // Trying to signout an account that hasn't signed in first should fail. + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->ClearPrimaryAccount( + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC)); + + // Adding an account without signing in should yield similar a result. + AccountInfo primary_account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(primary_account_mutator->ClearPrimaryAccount( + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC)); +} + +TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_Default) { + base::test::ScopedTaskEnvironment task_environment; + signin::IdentityTestEnvironment environment; + + signin::IdentityManager* identity_manager = environment.identity_manager(); + signin::PrimaryAccountMutator* primary_account_mutator = + identity_manager->GetPrimaryAccountMutator(); + + // Abort the test if the current platform does not support mutation of the + // primary account (the returned PrimaryAccountMutator* will be null). + if (!primary_account_mutator) + return; + + // This test requires two accounts to be made available. + AccountInfo primary_account_info = + environment.MakeAccountAvailable(kPrimaryAccountEmail); + AccountInfo other_account_info = + environment.MakeAccountAvailable(kAnotherAccountEmail); + + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( + other_account_info.account_id)); + + // Sign in the primary account to check ClearPrimaryAccount() later on. + primary_account_mutator->SetPrimaryAccount(primary_account_info.account_id); + EXPECT_TRUE(identity_manager->HasPrimaryAccount()); + EXPECT_EQ(identity_manager->GetPrimaryAccountId(), + primary_account_info.account_id); + + EXPECT_TRUE(primary_account_mutator->ClearPrimaryAccount( + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + signin_metrics::SIGNOUT_TEST, + signin_metrics::SignoutDelete::IGNORE_METRIC)); + + // The underlying PrimaryAccountManager in IdentityTestEnvironment will be + // created with signin::AccountConsistencyMethod::kDisabled, which should + // result in ClearPrimaryAccount() removing all the tokens. + EXPECT_FALSE(identity_manager->HasPrimaryAccount()); + EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( + primary_account_info.account_id)); + EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( + other_account_info.account_id)); +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kKeepAll +// keep all tokens, independently of the account consistency method. +TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_KeepAll) { + for (signin::AccountConsistencyMethod account_consistency_method : + kTestedAccountConsistencyMethods) { + RunClearPrimaryAccountTest( + account_consistency_method, + signin::PrimaryAccountMutator::ClearAccountsAction::kKeepAll, + RemoveAccountExpectation::kKeepAll); + } +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kRemoveAll +// remove all tokens, independently of the account consistency method. +TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_RemoveAll) { + for (signin::AccountConsistencyMethod account_consistency_method : + kTestedAccountConsistencyMethods) { + RunClearPrimaryAccountTest( + account_consistency_method, + signin::PrimaryAccountMutator::ClearAccountsAction::kRemoveAll, + RemoveAccountExpectation::kRemoveAll); + } +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kDefault +// and AccountConsistencyMethod::kDisabled (notably != kDice) removes all +// tokens. +TEST_F(PrimaryAccountMutatorTest, + ClearPrimaryAccount_Default_DisabledConsistency) { + RunClearPrimaryAccountTest( + signin::AccountConsistencyMethod::kDisabled, + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + RemoveAccountExpectation::kRemoveAll); +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kDefault +// and AccountConsistencyMethod::kMirror (notably != kDice) removes all +// tokens. +TEST_F(PrimaryAccountMutatorTest, + ClearPrimaryAccount_Default_MirrorConsistency) { + RunClearPrimaryAccountTest( + signin::AccountConsistencyMethod::kMirror, + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + RemoveAccountExpectation::kRemoveAll); +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kDefault +// and AccountConsistencyMethod::kDice keeps all accounts when the the primary +// account does not have an authentication error (see *_AuthError test). +TEST_F(PrimaryAccountMutatorTest, ClearPrimaryAccount_Default_DiceConsistency) { + RunClearPrimaryAccountTest( + signin::AccountConsistencyMethod::kDice, + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + RemoveAccountExpectation::kKeepAll); +} + +// Test that ClearPrimaryAccount(...) with ClearAccountTokensAction::kDefault +// and AccountConsistencyMethod::kDice removes *only* the primary account +// due to it authentication error. +TEST_F(PrimaryAccountMutatorTest, + ClearPrimaryAccount_Default_DiceConsistency_AuthError) { + RunClearPrimaryAccountTest( + signin::AccountConsistencyMethod::kDice, + signin::PrimaryAccountMutator::ClearAccountsAction::kDefault, + RemoveAccountExpectation::kRemovePrimary, AuthExpectation::kAuthError); +} +#endif // !defined(OS_CHROMEOS) diff --git a/chromium/components/signin/core/browser/set_accounts_in_cookie_result.h b/chromium/components/signin/public/identity_manager/set_accounts_in_cookie_result.h index 6adba69ab8f..c5e404402be 100644 --- a/chromium/components/signin/core/browser/set_accounts_in_cookie_result.h +++ b/chromium/components/signin/public/identity_manager/set_accounts_in_cookie_result.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_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ namespace signin { @@ -19,4 +19,4 @@ enum class SetAccountsInCookieResult { } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ diff --git a/chromium/components/signin/public/identity_manager/test_identity_manager_observer.cc b/chromium/components/signin/public/identity_manager/test_identity_manager_observer.cc new file mode 100644 index 00000000000..afa01cb7176 --- /dev/null +++ b/chromium/components/signin/public/identity_manager/test_identity_manager_observer.cc @@ -0,0 +1,229 @@ +// 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/public/identity_manager/test_identity_manager_observer.h" + +#include "testing/gtest/include/gtest/gtest.h" + +#include <utility> + +namespace signin { + +TestIdentityManagerObserver::TestIdentityManagerObserver( + IdentityManager* identity_manager) + : identity_manager_(identity_manager) { + identity_manager_->AddObserver(this); +} + +TestIdentityManagerObserver::~TestIdentityManagerObserver() { + identity_manager_->RemoveObserver(this); +} + +void TestIdentityManagerObserver::SetOnPrimaryAccountSetCallback( + base::OnceClosure callback) { + on_primary_account_set_callback_ = std::move(callback); +} + +const CoreAccountInfo& +TestIdentityManagerObserver::PrimaryAccountFromSetCallback() { + return primary_account_from_set_callback_; +} + +void TestIdentityManagerObserver::SetOnPrimaryAccountClearedCallback( + base::OnceClosure callback) { + on_primary_account_cleared_callback_ = std::move(callback); +} + +const CoreAccountInfo& +TestIdentityManagerObserver::PrimaryAccountFromClearedCallback() { + return primary_account_from_cleared_callback_; +} + +void TestIdentityManagerObserver::SetOnUnconsentedPrimaryAccountChangedCallback( + base::OnceClosure callback) { + on_unconsented_primary_account_callback_ = std::move(callback); +} + +const CoreAccountInfo& +TestIdentityManagerObserver::UnconsentedPrimaryAccountFromCallback() { + return unconsented_primary_account_from_callback_; +} + +void TestIdentityManagerObserver::SetOnRefreshTokenUpdatedCallback( + base::OnceClosure callback) { + on_refresh_token_updated_callback_ = std::move(callback); +} + +const CoreAccountInfo& +TestIdentityManagerObserver::AccountFromRefreshTokenUpdatedCallback() { + return account_from_refresh_token_updated_callback_; +} + +void TestIdentityManagerObserver::SetOnErrorStateOfRefreshTokenUpdatedCallback( + base::OnceClosure callback) { + on_error_state_of_refresh_token_updated_callback_ = std::move(callback); +} + +const CoreAccountInfo& TestIdentityManagerObserver:: + AccountFromErrorStateOfRefreshTokenUpdatedCallback() { + return account_from_error_state_of_refresh_token_updated_callback_; +} + +const GoogleServiceAuthError& +TestIdentityManagerObserver::ErrorFromErrorStateOfRefreshTokenUpdatedCallback() + const { + return error_from_error_state_of_refresh_token_updated_callback_; +} + +void TestIdentityManagerObserver::SetOnRefreshTokenRemovedCallback( + base::OnceClosure callback) { + on_refresh_token_removed_callback_ = std::move(callback); +} + +const CoreAccountId& +TestIdentityManagerObserver::AccountIdFromRefreshTokenRemovedCallback() { + return account_from_refresh_token_removed_callback_; +} + +void TestIdentityManagerObserver::SetOnRefreshTokensLoadedCallback( + base::OnceClosure callback) { + on_refresh_tokens_loaded_callback_ = std::move(callback); +} + +void TestIdentityManagerObserver::SetOnAccountsInCookieUpdatedCallback( + base::OnceClosure callback) { + on_accounts_in_cookie_updated_callback_ = std::move(callback); +} + +const AccountsInCookieJarInfo& +TestIdentityManagerObserver::AccountsInfoFromAccountsInCookieUpdatedCallback() { + return accounts_info_from_cookie_change_callback_; +} + +const GoogleServiceAuthError& +TestIdentityManagerObserver::ErrorFromAccountsInCookieUpdatedCallback() const { + return error_from_cookie_change_callback_; +} + +void TestIdentityManagerObserver::SetOnCookieDeletedByUserCallback( + base::OnceClosure callback) { + on_cookie_deleted_by_user_callback_ = std::move(callback); +} + +const AccountInfo& +TestIdentityManagerObserver::AccountFromAccountUpdatedCallback() { + return account_from_account_updated_callback_; +} + +const AccountInfo& +TestIdentityManagerObserver::AccountFromAccountRemovedWithInfoCallback() { + return account_from_account_removed_with_info_callback_; +} + +bool TestIdentityManagerObserver::WasCalledAccountRemovedWithInfoCallback() { + return was_called_account_removed_with_info_callback_; +} + +// Each element represents all the changes from an individual batch that has +// occurred, with the elements ordered from oldest to newest batch occurrence. +const std::vector<std::vector<CoreAccountId>>& +TestIdentityManagerObserver::BatchChangeRecords() const { + return batch_change_records_; +} + +// IdentityManager::Observer: +void TestIdentityManagerObserver::OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) { + primary_account_from_set_callback_ = primary_account_info; + if (on_primary_account_set_callback_) + std::move(on_primary_account_set_callback_).Run(); +} + +void TestIdentityManagerObserver::OnPrimaryAccountCleared( + const CoreAccountInfo& previous_primary_account_info) { + primary_account_from_cleared_callback_ = previous_primary_account_info; + if (on_primary_account_cleared_callback_) + std::move(on_primary_account_cleared_callback_).Run(); +} + +void TestIdentityManagerObserver::OnUnconsentedPrimaryAccountChanged( + const CoreAccountInfo& unconsented_primary_account_info) { + unconsented_primary_account_from_callback_ = unconsented_primary_account_info; + if (on_unconsented_primary_account_callback_) + std::move(on_unconsented_primary_account_callback_).Run(); +} + +void TestIdentityManagerObserver::OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) { + if (!is_inside_batch_) + StartBatchOfRefreshTokenStateChanges(); + + batch_change_records_.rbegin()->emplace_back(account_info.account_id); + account_from_refresh_token_updated_callback_ = account_info; + if (on_refresh_token_updated_callback_) + std::move(on_refresh_token_updated_callback_).Run(); +} + +void TestIdentityManagerObserver::OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) { + if (!is_inside_batch_) + StartBatchOfRefreshTokenStateChanges(); + + batch_change_records_.rbegin()->emplace_back(account_id); + account_from_refresh_token_removed_callback_ = account_id; + if (on_refresh_token_removed_callback_) + std::move(on_refresh_token_removed_callback_).Run(); +} + +void TestIdentityManagerObserver::OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info, + const GoogleServiceAuthError& error) { + account_from_error_state_of_refresh_token_updated_callback_ = account_info; + error_from_error_state_of_refresh_token_updated_callback_ = error; + if (on_error_state_of_refresh_token_updated_callback_) + std::move(on_error_state_of_refresh_token_updated_callback_).Run(); +} + +void TestIdentityManagerObserver::OnRefreshTokensLoaded() { + if (on_refresh_tokens_loaded_callback_) + std::move(on_refresh_tokens_loaded_callback_).Run(); +} + +void TestIdentityManagerObserver::OnAccountsInCookieUpdated( + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const GoogleServiceAuthError& error) { + accounts_info_from_cookie_change_callback_ = accounts_in_cookie_jar_info; + error_from_cookie_change_callback_ = error; + if (on_accounts_in_cookie_updated_callback_) + std::move(on_accounts_in_cookie_updated_callback_).Run(); +} + +void TestIdentityManagerObserver::OnAccountsCookieDeletedByUserAction() { + std::move(on_cookie_deleted_by_user_callback_).Run(); +} + +void TestIdentityManagerObserver::OnExtendedAccountInfoUpdated( + const AccountInfo& info) { + account_from_account_updated_callback_ = info; +} + +void TestIdentityManagerObserver::OnExtendedAccountInfoRemoved( + const AccountInfo& info) { + was_called_account_removed_with_info_callback_ = true; + account_from_account_removed_with_info_callback_ = info; +} + +void TestIdentityManagerObserver::StartBatchOfRefreshTokenStateChanges() { + EXPECT_FALSE(is_inside_batch_); + is_inside_batch_ = true; + + // Start a new batch. + batch_change_records_.emplace_back(std::vector<CoreAccountId>()); +} + +void TestIdentityManagerObserver::OnEndBatchOfRefreshTokenStateChanges() { + is_inside_batch_ = false; +} + +} // namespace signin diff --git a/chromium/components/signin/public/identity_manager/test_identity_manager_observer.h b/chromium/components/signin/public/identity_manager/test_identity_manager_observer.h new file mode 100644 index 00000000000..506e2461d6f --- /dev/null +++ b/chromium/components/signin/public/identity_manager/test_identity_manager_observer.h @@ -0,0 +1,135 @@ +// 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_TEST_IDENTITY_MANAGER_OBSERVER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_TEST_IDENTITY_MANAGER_OBSERVER_H_ + +#include <vector> + +#include "base/callback.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "google_apis/gaia/google_service_auth_error.h" + +namespace signin { + +// Class that observes events from IdentityManager. It allows setting +// |OnceClosure| callbacks to be executed for the observed events and retrieving +// the potential results and/or errors returned after such events have occurred. +class TestIdentityManagerObserver : IdentityManager::Observer { + public: + explicit TestIdentityManagerObserver(IdentityManager* identity_manager); + ~TestIdentityManagerObserver() override; + + void SetOnPrimaryAccountSetCallback(base::OnceClosure callback); + const CoreAccountInfo& PrimaryAccountFromSetCallback(); + + void SetOnPrimaryAccountClearedCallback(base::OnceClosure callback); + const CoreAccountInfo& PrimaryAccountFromClearedCallback(); + + void SetOnUnconsentedPrimaryAccountChangedCallback( + base::OnceClosure callback); + const CoreAccountInfo& UnconsentedPrimaryAccountFromCallback(); + + void SetOnRefreshTokenUpdatedCallback(base::OnceClosure callback); + const CoreAccountInfo& AccountFromRefreshTokenUpdatedCallback(); + + void SetOnErrorStateOfRefreshTokenUpdatedCallback(base::OnceClosure callback); + const CoreAccountInfo& AccountFromErrorStateOfRefreshTokenUpdatedCallback(); + const GoogleServiceAuthError& + ErrorFromErrorStateOfRefreshTokenUpdatedCallback() const; + + void SetOnRefreshTokenRemovedCallback(base::OnceClosure callback); + const CoreAccountId& AccountIdFromRefreshTokenRemovedCallback(); + + void SetOnRefreshTokensLoadedCallback(base::OnceClosure callback); + + void SetOnAccountsInCookieUpdatedCallback(base::OnceClosure callback); + const AccountsInCookieJarInfo& + AccountsInfoFromAccountsInCookieUpdatedCallback(); + const GoogleServiceAuthError& ErrorFromAccountsInCookieUpdatedCallback() + const; + + void SetOnCookieDeletedByUserCallback(base::OnceClosure callback); + + const AccountInfo& AccountFromAccountUpdatedCallback(); + const AccountInfo& AccountFromAccountRemovedWithInfoCallback(); + bool WasCalledAccountRemovedWithInfoCallback(); + + // Each element represents all the changes from an individual batch that has + // occurred, with the elements ordered from oldest to newest batch occurrence. + const std::vector<std::vector<CoreAccountId>>& BatchChangeRecords() const; + + private: + // IdentityManager::Observer: + void OnPrimaryAccountSet( + const CoreAccountInfo& primary_account_info) override; + void OnPrimaryAccountCleared( + const CoreAccountInfo& previous_primary_account_info) override; + void OnUnconsentedPrimaryAccountChanged( + const CoreAccountInfo& unconsented_primary_account_info) override; + void OnRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info) override; + void OnRefreshTokenRemovedForAccount( + const CoreAccountId& account_id) override; + void OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info, + const GoogleServiceAuthError& error) override; + void OnRefreshTokensLoaded() override; + + void OnAccountsInCookieUpdated( + const AccountsInCookieJarInfo& accounts_in_cookie_jar_info, + const GoogleServiceAuthError& error) override; + void OnAccountsCookieDeletedByUserAction() override; + + void OnExtendedAccountInfoUpdated(const AccountInfo& info) override; + void OnExtendedAccountInfoRemoved(const AccountInfo& info) override; + + void StartBatchOfRefreshTokenStateChanges(); + void OnEndBatchOfRefreshTokenStateChanges() override; + + IdentityManager* identity_manager_; + + base::OnceClosure on_primary_account_set_callback_; + CoreAccountInfo primary_account_from_set_callback_; + + base::OnceClosure on_primary_account_cleared_callback_; + CoreAccountInfo primary_account_from_cleared_callback_; + + base::OnceClosure on_unconsented_primary_account_callback_; + CoreAccountInfo unconsented_primary_account_from_callback_; + + base::OnceClosure on_refresh_token_updated_callback_; + CoreAccountInfo account_from_refresh_token_updated_callback_; + + base::OnceClosure on_error_state_of_refresh_token_updated_callback_; + CoreAccountInfo account_from_error_state_of_refresh_token_updated_callback_; + GoogleServiceAuthError + error_from_error_state_of_refresh_token_updated_callback_; + + base::OnceClosure on_refresh_token_removed_callback_; + CoreAccountId account_from_refresh_token_removed_callback_; + + base::OnceClosure on_refresh_tokens_loaded_callback_; + + base::OnceClosure on_accounts_in_cookie_updated_callback_; + AccountsInCookieJarInfo accounts_info_from_cookie_change_callback_; + GoogleServiceAuthError error_from_cookie_change_callback_; + + base::OnceClosure on_cookie_deleted_by_user_callback_; + + AccountInfo account_from_account_updated_callback_; + AccountInfo account_from_account_removed_with_info_callback_; + + bool is_inside_batch_ = false; + bool was_called_account_removed_with_info_callback_ = false; + std::vector<std::vector<CoreAccountId>> batch_change_records_; + + DISALLOW_COPY_AND_ASSIGN(TestIdentityManagerObserver); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_TEST_IDENTITY_MANAGER_OBSERVER_H_ diff --git a/chromium/components/signin/core/browser/ubertoken_fetcher.cc b/chromium/components/signin/public/identity_manager/ubertoken_fetcher.cc index 6eea8ba9c4d..e3377be94e1 100644 --- a/chromium/components/signin/core/browser/ubertoken_fetcher.cc +++ b/chromium/components/signin/public/identity_manager/ubertoken_fetcher.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/core/browser/ubertoken_fetcher.h" +#include "components/signin/public/identity_manager/ubertoken_fetcher.h" namespace signin { diff --git a/chromium/components/signin/core/browser/ubertoken_fetcher.h b/chromium/components/signin/public/identity_manager/ubertoken_fetcher.h index cc4f1691e5c..f79d635d9a8 100644 --- a/chromium/components/signin/core/browser/ubertoken_fetcher.h +++ b/chromium/components/signin/public/identity_manager/ubertoken_fetcher.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_CORE_BROWSER_UBERTOKEN_FETCHER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_UBERTOKEN_FETCHER_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_UBERTOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_UBERTOKEN_FETCHER_H_ #include <memory> @@ -32,4 +32,4 @@ class UbertokenFetcher { } // namespace signin -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_UBERTOKEN_FETCHER_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_UBERTOKEN_FETCHER_H_ diff --git a/chromium/components/signin/public/webdata/BUILD.gn b/chromium/components/signin/public/webdata/BUILD.gn new file mode 100644 index 00000000000..a301f77272d --- /dev/null +++ b/chromium/components/signin/public/webdata/BUILD.gn @@ -0,0 +1,38 @@ +# 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. + +# This target cannot depend on anything else from //components/signin. +static_library("webdata") { + sources = [ + "token_service_table.cc", + "token_service_table.h", + "token_web_data.cc", + "token_web_data.h", + ] + + deps = [ + "//components/os_crypt", + "//sql", + ] + + public_deps = [ + "//base", + "//components/webdata/common", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "token_service_table_unittest.cc", + ] + + deps = [ + ":webdata", + "//base/test:test_support", + "//components/os_crypt:test_support", + "//components/webdata/common", + "//testing/gtest", + ] +} diff --git a/chromium/components/signin/core/browser/webdata/token_service_table.cc b/chromium/components/signin/public/webdata/token_service_table.cc index e938a5f2a12..4e3be5adde4 100644 --- a/chromium/components/signin/core/browser/webdata/token_service_table.cc +++ b/chromium/components/signin/public/webdata/token_service_table.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/core/browser/webdata/token_service_table.h" +#include "components/signin/public/webdata/token_service_table.h" #include <map> #include <string> @@ -34,7 +34,6 @@ enum ReadOneTokenResult { TokenServiceTable* TokenServiceTable::FromWebDatabase(WebDatabase* db) { return static_cast<TokenServiceTable*>(db->GetTable(GetKey())); - } WebDatabaseTable::TypeKey TokenServiceTable::GetTypeKey() const { @@ -73,8 +72,8 @@ bool TokenServiceTable::RemoveAllTokens() { } bool TokenServiceTable::RemoveTokenForService(const std::string& service) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM token_service WHERE service = ?")); + sql::Statement s( + db_->GetUniqueStatement("DELETE FROM token_service WHERE service = ?")); s.BindString(0, service); bool result = s.Run(); @@ -93,9 +92,9 @@ bool TokenServiceTable::SetTokenForService(const std::string& service, // Don't bother with a cached statement since this will be a relatively // infrequent operation. - sql::Statement s(db_->GetUniqueStatement( - "INSERT OR REPLACE INTO token_service " - "(service, encrypted_token) VALUES (?, ?)")); + sql::Statement s( + db_->GetUniqueStatement("INSERT OR REPLACE INTO token_service " + "(service, encrypted_token) VALUES (?, ?)")); s.BindString(0, service); s.BindBlob(1, encrypted_token.data(), static_cast<int>(encrypted_token.length())); @@ -128,8 +127,8 @@ TokenServiceTable::Result TokenServiceTable::GetAllTokens( std::string decrypted_token; std::string service; service = s.ColumnString(0); - bool entry_ok = !service.empty() && - s.ColumnBlobAsString(1, &encrypted_token); + bool entry_ok = + !service.empty() && s.ColumnBlobAsString(1, &encrypted_token); if (entry_ok) { if (OSCrypt::DecryptString(encrypted_token, &decrypted_token)) { (*tokens)[service] = decrypted_token; @@ -149,8 +148,7 @@ TokenServiceTable::Result TokenServiceTable::GetAllTokens( } DCHECK_LT(read_token_result, READ_ONE_TOKEN_MAX_VALUE); UMA_HISTOGRAM_ENUMERATION("Signin.TokenTable.ReadTokenFromDBResult", - read_token_result, - READ_ONE_TOKEN_MAX_VALUE); + read_token_result, READ_ONE_TOKEN_MAX_VALUE); } VLOG(1) << "Loaded tokens: result = " << read_all_tokens_result << " ; number of tokens loaded = " << number_of_tokens_loaded; diff --git a/chromium/components/signin/core/browser/webdata/token_service_table.h b/chromium/components/signin/public/webdata/token_service_table.h index b6d750f0ce5..28ae99a80a8 100644 --- a/chromium/components/signin/core/browser/webdata/token_service_table.h +++ b/chromium/components/signin/public/webdata/token_service_table.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_CORE_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ +#ifndef COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_SERVICE_TABLE_H_ +#define COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_SERVICE_TABLE_H_ #include <map> #include <string> @@ -48,11 +48,10 @@ class TokenServiceTable : public WebDatabaseTable { // Store a token in the token_service table. Stored encrypted. May cause // a mac keychain popup. // True if we encrypted a token and stored it, false otherwise. - bool SetTokenForService(const std::string& service, - const std::string& token); + bool SetTokenForService(const std::string& service, const std::string& token); private: DISALLOW_COPY_AND_ASSIGN(TokenServiceTable); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ +#endif // COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_SERVICE_TABLE_H_ diff --git a/chromium/components/signin/core/browser/webdata/token_service_table_unittest.cc b/chromium/components/signin/public/webdata/token_service_table_unittest.cc index 32d169a839a..9a3332d5481 100644 --- a/chromium/components/signin/core/browser/webdata/token_service_table_unittest.cc +++ b/chromium/components/signin/public/webdata/token_service_table_unittest.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "components/signin/public/webdata/token_service_table.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/os_crypt/os_crypt_mocker.h" -#include "components/signin/core/browser/webdata/token_service_table.h" #include "components/webdata/common/web_database.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/components/signin/core/browser/webdata/token_web_data.cc b/chromium/components/signin/public/webdata/token_web_data.cc index cc38acaa09b..9ecd6b68624 100644 --- a/chromium/components/signin/core/browser/webdata/token_web_data.cc +++ b/chromium/components/signin/public/webdata/token_web_data.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/core/browser/webdata/token_web_data.h" +#include "components/signin/public/webdata/token_web_data.h" #include <memory> @@ -10,7 +10,7 @@ #include "base/memory/ref_counted_delete_on_sequence.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" -#include "components/signin/core/browser/webdata/token_service_table.h" +#include "components/signin/public/webdata/token_service_table.h" #include "components/webdata/common/web_database_service.h" using base::Bind; @@ -30,17 +30,18 @@ class TokenWebDataBackend return WebDatabase::COMMIT_NOT_NEEDED; } - WebDatabase::State RemoveTokenForService( - const std::string& service, WebDatabase* db) { - if (TokenServiceTable::FromWebDatabase(db) - ->RemoveTokenForService(service)) { + WebDatabase::State RemoveTokenForService(const std::string& service, + WebDatabase* db) { + if (TokenServiceTable::FromWebDatabase(db)->RemoveTokenForService( + service)) { return WebDatabase::COMMIT_NEEDED; } return WebDatabase::COMMIT_NOT_NEEDED; } - WebDatabase::State SetTokenForService( - const std::string& service, const std::string& token, WebDatabase* db) { + WebDatabase::State SetTokenForService(const std::string& service, + const std::string& token, + WebDatabase* db) { if (TokenServiceTable::FromWebDatabase(db)->SetTokenForService(service, token)) { return WebDatabase::COMMIT_NEEDED; @@ -56,8 +57,7 @@ class TokenWebDataBackend } protected: - virtual ~TokenWebDataBackend() { - } + virtual ~TokenWebDataBackend() {} private: friend class base::RefCountedDeleteOnSequence<TokenWebDataBackend>; @@ -79,27 +79,28 @@ TokenWebData::TokenWebData( void TokenWebData::SetTokenForService(const std::string& service, const std::string& token) { - wdbs_->ScheduleDBTask(FROM_HERE, - Bind(&TokenWebDataBackend::SetTokenForService, token_backend_, - service, token)); + wdbs_->ScheduleDBTask( + FROM_HERE, Bind(&TokenWebDataBackend::SetTokenForService, token_backend_, + service, token)); } void TokenWebData::RemoveAllTokens() { - wdbs_->ScheduleDBTask(FROM_HERE, - Bind(&TokenWebDataBackend::RemoveAllTokens, token_backend_)); + wdbs_->ScheduleDBTask( + FROM_HERE, Bind(&TokenWebDataBackend::RemoveAllTokens, token_backend_)); } void TokenWebData::RemoveTokenForService(const std::string& service) { wdbs_->ScheduleDBTask(FROM_HERE, - Bind(&TokenWebDataBackend::RemoveTokenForService, token_backend_, - service)); + Bind(&TokenWebDataBackend::RemoveTokenForService, + token_backend_, service)); } // Null on failure. Success is WDResult<std::string> WebDataServiceBase::Handle TokenWebData::GetAllTokens( WebDataServiceConsumer* consumer) { - return wdbs_->ScheduleDBTaskWithResult(FROM_HERE, - Bind(&TokenWebDataBackend::GetAllTokens, token_backend_), consumer); + return wdbs_->ScheduleDBTaskWithResult( + FROM_HERE, Bind(&TokenWebDataBackend::GetAllTokens, token_backend_), + consumer); } TokenWebData::TokenWebData( @@ -108,5 +109,4 @@ TokenWebData::TokenWebData( : WebDataServiceBase(nullptr, ProfileErrorCallback(), ui_task_runner), token_backend_(new TokenWebDataBackend(db_task_runner)) {} -TokenWebData::~TokenWebData() { -} +TokenWebData::~TokenWebData() {} diff --git a/chromium/components/signin/core/browser/webdata/token_web_data.h b/chromium/components/signin/public/webdata/token_web_data.h index 39bb6af92a9..2992fe6caef 100644 --- a/chromium/components/signin/core/browser/webdata/token_web_data.h +++ b/chromium/components/signin/public/webdata/token_web_data.h @@ -6,8 +6,8 @@ // information and MUST not be extracted, overwritten or modified except // through Chromium defined APIs. -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_WEBDATA_TOKEN_WEB_DATA_H__ -#define COMPONENTS_SIGNIN_CORE_BROWSER_WEBDATA_TOKEN_WEB_DATA_H__ +#ifndef COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_WEB_DATA_H_ +#define COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_WEB_DATA_H_ #include <map> #include <string> @@ -18,7 +18,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "components/signin/core/browser/webdata/token_service_table.h" +#include "components/signin/public/webdata/token_service_table.h" #include "components/webdata/common/web_data_results.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_data_service_consumer.h" @@ -55,8 +55,7 @@ class TokenWebData : public WebDataServiceBase { scoped_refptr<base::SingleThreadTaskRunner> db_task_runner); // Set a token to use for a specified service. - void SetTokenForService(const std::string& service, - const std::string& token); + void SetTokenForService(const std::string& service, const std::string& token); // Remove all tokens stored in the web database. void RemoveAllTokens(); @@ -79,4 +78,4 @@ class TokenWebData : public WebDataServiceBase { DISALLOW_COPY_AND_ASSIGN(TokenWebData); }; -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_WEBDATA_TOKEN_WEB_DATA_H__ +#endif // COMPONENTS_SIGNIN_PUBLIC_WEBDATA_TOKEN_WEB_DATA_H_ |