[SigninPrefs] Create the main structure for Accounts SigninPrefs
Add a structure that takes care of all the prefs related to accounts.
Accounts are mapped by their GaiaId which is used as the key of the
main dictionary prefs. The account dictionary pref will hold the
specific data prefs that will be populated in a follow up change.
The structure also allows the deletion of those prefs.
This management is expected to be controlled by changes done
through the IdentityManager. Attached those calls to the
GaiaInfoUpdateService as it already listens to most needed calls and
already update the ProfileAttributesStorage.
The creation of the prefs is done in a lazy mode. Prefs dictionary
and pref data are created when the values are set for the first time.
The GaiaIds used to create the prefs are expected to be valid, point
to a valid account in Chrome. Otherwise the pref, even though it will
be created at first, will probably be deleted after a call to
/ListAccount.
The structure could actually be replaced by some free functions since
it is currently not held anywhere or doesn't hold any independent
run time information, but restricting all access to a class scope
allows to maintain a more coupled relation with the different future
usages and allows for easier extension, such as listening to
preference changes for example that might be needed later on.
The code done is very similar to what is already done in SyncPrefs,
however it is not big/complex enough to actually share the code,
which would have made the logic more complex than it is. Also the
underlying need will have different usages, duplicating some simple
parts of the code is acceptable in this case since it is mainly
around container manipulation.
Bug: b:331767195, b:335437309
Change-Id: Ia209e5de1020bfb012d4cdc8ad43b28e3b393b73
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5487922
Reviewed-by: David Roger <[email protected]>
Commit-Queue: Ryan Sultanem <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1293739}
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index e1186a23..09569ecc 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -166,6 +166,7 @@
#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/sessions/core/session_id_generator.h"
#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/base/signin_prefs.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/subresource_filter/content/shared/browser/ruleset_service.h"
@@ -1893,6 +1894,7 @@
registry);
SessionStartupPref::RegisterProfilePrefs(registry);
SharingSyncPreference::RegisterProfilePrefs(registry);
+ SigninPrefs::RegisterProfilePrefs(registry);
site_engagement::SiteEngagementService::RegisterProfilePrefs(registry);
supervised_user::RegisterProfilePrefs(registry);
sync_sessions::SessionSyncPrefs::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/profiles/gaia_info_update_service.cc b/chrome/browser/profiles/gaia_info_update_service.cc
index 9e18ea17..9cbfaea 100644
--- a/chrome/browser/profiles/gaia_info_update_service.cc
+++ b/chrome/browser/profiles/gaia_info_update_service.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include "base/containers/contains.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
@@ -19,18 +20,66 @@
#include "components/signin/public/base/avatar_icon_util.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/base/signin_prefs.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 "content/public/browser/storage_partition.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
+namespace {
+
+void UpdateAccountsPrefs(
+ PrefService& pref_service,
+ const signin::IdentityManager& identity_manager,
+ const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info) {
+ if (!accounts_in_cookie_jar_info.accounts_are_fresh) {
+ return;
+ }
+
+ // Get all accounts in Chrome; both signed in and signed out accounts.
+ base::flat_set<SigninPrefs::GaiaId> account_ids_in_chrome;
+ for (const auto& account : accounts_in_cookie_jar_info.signed_in_accounts) {
+ account_ids_in_chrome.insert(account.gaia_id);
+ }
+ for (const auto& account : accounts_in_cookie_jar_info.signed_out_accounts) {
+ account_ids_in_chrome.insert(account.gaia_id);
+ }
+
+ // If there is a Primary account, also keep it even if it was removed (not in
+ // the cookie jar at all).
+ // Note: Make sure that `primary_account_info` and `account_ids_in_chrome`
+ // have the same lifetime, since `IdentityManager::GetPrimaryAccountInfo()`
+ // returns a copy, and `account_ids_in_chrome` is a set of
+ // `SigninPrefs::GaiaId` which are `std::string_view` (references); in order
+ // for the reference not to outlive the actual string.
+ CoreAccountInfo primary_account_info =
+ identity_manager.GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+ if (!primary_account_info.IsEmpty()) {
+ // Set will make sure it is not duplicated if already added.
+ account_ids_in_chrome.insert(primary_account_info.gaia);
+ }
+ // TODO(b/331767195): In case the prefs are needed for ChromeOS and Android
+ // (platforms where the account is tied to the OS) in the future, we would
+ // also need to keep the accounts that have an AccountInfo that is still
+ // present in Chrome (accounts that have refresh tokens) in addition to the
+ // above checks on cookies and primary account.
+
+ SigninPrefs signin_prefs(pref_service);
+ signin_prefs.RemoveAllAccountPrefsExcept(account_ids_in_chrome);
+}
+
+} // namespace
+
GAIAInfoUpdateService::GAIAInfoUpdateService(
signin::IdentityManager* identity_manager,
ProfileAttributesStorage* profile_attributes_storage,
+ PrefService& pref_service,
const base::FilePath& profile_path)
: identity_manager_(identity_manager),
profile_attributes_storage_(profile_attributes_storage),
+ pref_service_(pref_service),
profile_path_(profile_path) {
identity_manager_->AddObserver(this);
@@ -130,6 +179,11 @@
break;
case signin::PrimaryAccountChangeEvent::Type::kCleared:
ClearProfileEntry();
+
+ // When clearing the primary account, if the account is already removed
+ // from the cookie jar, we should remove the prefs as well.
+ UpdateAccountsPrefs(pref_service_.get(), *identity_manager_,
+ identity_manager_->GetAccountsInCookieJar());
break;
case signin::PrimaryAccountChangeEvent::Type::kNone:
break;
@@ -176,6 +230,9 @@
identity_manager_->FindExtendedAccountInfoByAccountId(account.id));
}
}
+
+ UpdateAccountsPrefs(pref_service_.get(), *identity_manager_,
+ accounts_in_cookie_jar_info);
}
bool GAIAInfoUpdateService::ShouldUpdatePrimaryAccount() {
diff --git a/chrome/browser/profiles/gaia_info_update_service.h b/chrome/browser/profiles/gaia_info_update_service.h
index 68f7b46..a214719 100644
--- a/chrome/browser/profiles/gaia_info_update_service.h
+++ b/chrome/browser/profiles/gaia_info_update_service.h
@@ -17,11 +17,13 @@
// This service kicks off a download of the user's name and profile picture.
// The results are saved in the profile info cache.
+// It also manages the lifecycle of the signin accounts prefs.
class GAIAInfoUpdateService : public KeyedService,
public signin::IdentityManager::Observer {
public:
GAIAInfoUpdateService(signin::IdentityManager* identity_manager,
ProfileAttributesStorage* profile_attributes_storage,
+ PrefService& pref_service,
const base::FilePath& profile_path);
GAIAInfoUpdateService(const GAIAInfoUpdateService&) = delete;
@@ -52,6 +54,7 @@
raw_ptr<signin::IdentityManager> identity_manager_;
raw_ptr<ProfileAttributesStorage> profile_attributes_storage_;
+ raw_ref<PrefService> pref_service_;
const base::FilePath profile_path_;
// TODO(msalama): remove when |SigninProfileAttributesUpdater| is folded into
// |GAIAInfoUpdateService|.
diff --git a/chrome/browser/profiles/gaia_info_update_service_factory.cc b/chrome/browser/profiles/gaia_info_update_service_factory.cc
index 8d37760..c1626a3 100644
--- a/chrome/browser/profiles/gaia_info_update_service_factory.cc
+++ b/chrome/browser/profiles/gaia_info_update_service_factory.cc
@@ -51,7 +51,7 @@
return std::make_unique<GAIAInfoUpdateService>(
IdentityManagerFactory::GetForProfile(profile),
&g_browser_process->profile_manager()->GetProfileAttributesStorage(),
- profile->GetPath());
+ *profile->GetPrefs(), profile->GetPath());
}
bool GAIAInfoUpdateServiceFactory::ServiceIsNULLWhileTesting() const {
diff --git a/chrome/browser/profiles/gaia_info_update_service_unittest.cc b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
index 93656081..a808f3d 100644
--- a/chrome/browser/profiles/gaia_info_update_service_unittest.cc
+++ b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
@@ -28,11 +28,12 @@
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/pref_service.h"
#include "components/profile_metrics/state.h"
+#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/base/signin_prefs.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_unittest_util.h"
@@ -62,13 +63,18 @@
const char kChromiumOrgDomain[] = "chromium.org";
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
+} // namespace
+
class GAIAInfoUpdateServiceTest : public testing::Test {
protected:
GAIAInfoUpdateServiceTest()
: testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
- identity_test_env_(/*test_url_loader_factory=*/nullptr,
- /*pref_service=*/nullptr,
- /*test_signin_client=*/nullptr) {}
+ identity_test_env_(
+ /*test_url_loader_factory=*/nullptr,
+ /*pref_service=*/nullptr,
+ /*test_signin_client=*/nullptr) {
+ SigninPrefs::RegisterProfilePrefs(pref_service_.registry());
+ }
GAIAInfoUpdateServiceTest(const GAIAInfoUpdateServiceTest&) = delete;
GAIAInfoUpdateServiceTest& operator=(const GAIAInfoUpdateServiceTest&) =
@@ -88,7 +94,7 @@
service_ = std::make_unique<GAIAInfoUpdateService>(
identity_test_env_.identity_manager(),
- testing_profile_manager_.profile_attributes_storage(),
+ testing_profile_manager_.profile_attributes_storage(), pref_service_,
profile()->GetPath());
}
@@ -121,13 +127,22 @@
base::UTF8ToUTF16(name), 0, TestingProfile::TestingFactories());
}
+ bool HasAccountPrefs(const std::string& gaia_id) {
+ return SigninPrefs(pref_service_).HasAccountPrefs(gaia_id);
+ }
+
+ void InitializeAccountPref(const std::string& gaia_id) {
+ // 5 is just a random value to create the pref.
+ SigninPrefs(pref_service_).SetDummyValue(gaia_id, 5);
+ }
+
content::BrowserTaskEnvironment task_environment_;
TestingProfileManager testing_profile_manager_;
raw_ptr<TestingProfile> profile_ = nullptr;
signin::IdentityTestEnvironment identity_test_env_;
+ TestingPrefServiceSimple pref_service_;
std::unique_ptr<GAIAInfoUpdateService> service_;
};
-} // namespace
TEST_F(GAIAInfoUpdateServiceTest, SyncOnSyncOff) {
AccountInfo info =
@@ -349,3 +364,99 @@
EXPECT_FALSE(entry->GetGAIAPicture());
EXPECT_TRUE(entry->GetHostedDomain().empty());
}
+
+TEST_F(GAIAInfoUpdateServiceTest,
+ SigninPrefsWithSignedInAccountAndSecondaryAccount) {
+ const std::string primary_gaia_id = "primary_gaia_id";
+ ASSERT_FALSE(HasAccountPrefs(primary_gaia_id));
+
+ AccountInfo primary_info = identity_test_env()->MakeAccountAvailable(
+ "[email protected]",
+ {.primary_account_consent_level = signin::ConsentLevel::kSignin,
+ .set_cookie = true,
+ .gaia_id = primary_gaia_id});
+ ASSERT_EQ(primary_gaia_id, primary_info.gaia);
+ InitializeAccountPref(primary_gaia_id);
+ EXPECT_TRUE(HasAccountPrefs(primary_gaia_id));
+
+ // Add a secondary account.
+ const std::string secondary_gaia_id = "secondary_gaia_id";
+ ASSERT_FALSE(HasAccountPrefs(secondary_gaia_id));
+ AccountInfo secondary_info = identity_test_env()->MakeAccountAvailable(
+ "[email protected]",
+ {.set_cookie = true, .gaia_id = secondary_gaia_id});
+ ASSERT_EQ(secondary_gaia_id, secondary_info.gaia);
+ InitializeAccountPref(secondary_gaia_id);
+ EXPECT_TRUE(HasAccountPrefs(secondary_gaia_id));
+
+ // Set the accounts as signed out.
+ identity_test_env()->SetCookieAccounts(
+ {{primary_info.email, primary_info.gaia, /*signed_out=*/true},
+ {secondary_info.email, secondary_info.gaia, /*signed_out=*/true}});
+ // Prefs should remain as the cookies are not cleared yet.
+ EXPECT_TRUE(HasAccountPrefs(primary_gaia_id));
+ EXPECT_TRUE(HasAccountPrefs(secondary_gaia_id));
+
+ // Clear all cookies.
+ identity_test_env()->SetCookieAccounts({});
+ ASSERT_TRUE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+ signin::ConsentLevel::kSignin));
+ // Primary account prefs should remain since the account is still signed in.
+ EXPECT_TRUE(HasAccountPrefs(primary_gaia_id));
+ // Secondary account prefs should be cleared.
+ EXPECT_FALSE(HasAccountPrefs(secondary_gaia_id));
+
+ // Clearing primary account should now clear the account prefs as well since
+ // the cookie is already cleared.
+ identity_test_env()->ClearPrimaryAccount();
+ EXPECT_FALSE(HasAccountPrefs(primary_gaia_id));
+}
+
+TEST_F(GAIAInfoUpdateServiceTest, SigninPrefsWithSignedInWebOnly) {
+ const std::string gaia_id = "gaia_id";
+ ASSERT_FALSE(HasAccountPrefs(gaia_id));
+ AccountInfo info = identity_test_env()->MakeAccountAvailable(
+ "[email protected]", {.set_cookie = true, .gaia_id = gaia_id});
+ ASSERT_EQ(gaia_id, info.gaia);
+ ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+ signin::ConsentLevel::kSignin));
+ InitializeAccountPref(gaia_id);
+ EXPECT_TRUE(HasAccountPrefs(gaia_id));
+
+ // Web sign out keeps the prefs.
+ identity_test_env()->SetCookieAccounts(
+ {{info.email, info.gaia, /*signed_out=*/true}});
+ EXPECT_TRUE(HasAccountPrefs(gaia_id));
+
+ // Clearing the cookie removes the prefs.
+ identity_test_env()->SetCookieAccounts({});
+ EXPECT_FALSE(HasAccountPrefs(gaia_id));
+}
+
+TEST_F(GAIAInfoUpdateServiceTest, SigninPrefsWithGaiaIdNotInChrome) {
+ // Use an account in Chrome.
+ const std::string gaia_id = "gaia_id";
+ ASSERT_FALSE(HasAccountPrefs(gaia_id));
+ AccountInfo info = identity_test_env()->MakeAccountAvailable(
+ "[email protected]", {.set_cookie = true, .gaia_id = gaia_id});
+ ASSERT_EQ(gaia_id, info.gaia);
+ ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
+ signin::ConsentLevel::kSignin));
+ InitializeAccountPref(gaia_id);
+ ASSERT_TRUE(HasAccountPrefs(gaia_id));
+
+ // Use an account that is not in Chrome.
+ const std::string gaia_id_not_in_chrome = "gaia_id_not_in_chrome";
+ ASSERT_FALSE(HasAccountPrefs(gaia_id_not_in_chrome));
+
+ // This is possible even if the account is not in Chrome.
+ InitializeAccountPref(gaia_id_not_in_chrome);
+ EXPECT_TRUE(HasAccountPrefs(gaia_id_not_in_chrome));
+
+ // Refreshing the cookie jar should remove the account not in Chrome.
+ identity_test_env()->TriggerListAccount();
+
+ // Prefs for the Account in Chrome remains, not for the account not in Chrome.
+ EXPECT_TRUE(HasAccountPrefs(gaia_id));
+ EXPECT_FALSE(HasAccountPrefs(gaia_id_not_in_chrome));
+}