1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/profiles/gaia_info_update_service.h"
6 
7 #include <stddef.h>
8 
9 #include <string>
10 
11 #include "base/bind.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/test/metrics/histogram_tester.h"
14 #include "build/build_config.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/profiles/profile_attributes_entry.h"
17 #include "chrome/browser/profiles/profile_attributes_storage.h"
18 #include "chrome/browser/profiles/profile_downloader.h"
19 #include "chrome/browser/profiles/profile_info_cache.h"
20 #include "chrome/browser/profiles/profile_info_cache_unittest.h"
21 #include "chrome/browser/profiles/profiles_state.h"
22 #include "chrome/browser/signin/chrome_signin_client_factory.h"
23 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
24 #include "chrome/browser/signin/test_signin_client_builder.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/test/base/testing_browser_process.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "chrome/test/base/testing_profile_manager.h"
29 #include "components/prefs/pref_service.h"
30 #include "components/profile_metrics/state.h"
31 #include "components/signin/public/base/signin_pref_names.h"
32 #include "components/signin/public/identity_manager/account_info.h"
33 #include "components/sync_preferences/pref_service_syncable.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "ui/gfx/image/image.h"
36 #include "ui/gfx/image/image_unittest_util.h"
37 
38 using ::testing::Return;
39 
40 namespace {
41 
42 #if !defined(OS_CHROMEOS)
GetValidAccountInfo(std::string email,CoreAccountId account_id,std::string given_name,std::string full_name,std::string hosted_domain)43 AccountInfo GetValidAccountInfo(std::string email,
44                                 CoreAccountId account_id,
45                                 std::string given_name,
46                                 std::string full_name,
47                                 std::string hosted_domain) {
48   AccountInfo account_info;
49   account_info.email = email;
50   account_info.gaia = account_id.ToString();
51   account_info.account_id = account_id;
52   account_info.given_name = given_name;
53   account_info.full_name = full_name;
54   account_info.hosted_domain = hosted_domain;
55   account_info.locale = email;
56   account_info.picture_url = "example.com";
57   return account_info;
58 }
59 
60 #if !defined(ANDROID)
61 const char kChromiumOrgDomain[] = "chromium.org";
62 #endif  // !defined(ANDROID)
63 
64 #endif  // !defined(OS_CHROMEOS)
65 
66 class GAIAInfoUpdateServiceTest : public testing::Test {
67  protected:
GAIAInfoUpdateServiceTest()68   GAIAInfoUpdateServiceTest()
69       : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
70   ~GAIAInfoUpdateServiceTest() override = default;
71 
SetUp()72   void SetUp() override {
73     testing::Test::SetUp();
74     ASSERT_TRUE(testing_profile_manager_.SetUp());
75     RecreateGAIAInfoUpdateService();
76   }
77 
RecreateGAIAInfoUpdateService()78   void RecreateGAIAInfoUpdateService() {
79     if (service_)
80       service_->Shutdown();
81 
82     service_.reset(new GAIAInfoUpdateService(
83         identity_test_env_.identity_manager(),
84         testing_profile_manager_.profile_attributes_storage(),
85         profile()->GetPath(), profile()->GetPrefs()));
86   }
87 
TearDown()88   void TearDown() override {
89     if (service_) {
90       service_->Shutdown();
91       service_.reset();
92     }
93   }
94 
profile()95   TestingProfile* profile() {
96     if (!profile_)
97       CreateProfile("Person 1");
98     return profile_;
99   }
100 
identity_test_env()101   signin::IdentityTestEnvironment* identity_test_env() {
102     return &identity_test_env_;
103   }
104 
storage()105   ProfileAttributesStorage* storage() {
106     return testing_profile_manager_.profile_attributes_storage();
107   }
108 
service()109   GAIAInfoUpdateService* service() { return service_.get(); }
110 
CreateProfile(const std::string & name)111   void CreateProfile(const std::string& name) {
112     profile_ = testing_profile_manager_.CreateTestingProfile(
113         name, std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
114         base::UTF8ToUTF16(name), 0, std::string(),
115         TestingProfile::TestingFactories());
116   }
117 
118   content::BrowserTaskEnvironment task_environment_;
119   TestingProfileManager testing_profile_manager_;
120   TestingProfile* profile_ = nullptr;
121   signin::IdentityTestEnvironment identity_test_env_;
122   std::unique_ptr<GAIAInfoUpdateService> service_;
123 
124  private:
125   DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceTest);
126 };
127 }  // namespace
128 
TEST_F(GAIAInfoUpdateServiceTest,ShouldUseGAIAProfileInfo)129 TEST_F(GAIAInfoUpdateServiceTest, ShouldUseGAIAProfileInfo) {
130 #if defined(OS_CHROMEOS)
131   // This feature should never be enabled on ChromeOS.
132   EXPECT_FALSE(GAIAInfoUpdateService::ShouldUseGAIAProfileInfo(profile()));
133 #endif
134 }
135 
136 #if !defined(OS_CHROMEOS)
137 
TEST_F(GAIAInfoUpdateServiceTest,SyncOnSyncOff)138 TEST_F(GAIAInfoUpdateServiceTest, SyncOnSyncOff) {
139   AccountInfo info =
140       identity_test_env()->MakeAccountAvailable("pat@example.com");
141   base::RunLoop().RunUntilIdle();
142   identity_test_env()->SetPrimaryAccount(info.email);
143   info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo",
144                              kNoHostedDomainFound);
145   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
146                                       info);
147   base::RunLoop().RunUntilIdle();
148 
149   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
150   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
151   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
152   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
153   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
154   EXPECT_EQ(
155       profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
156       kNoHostedDomainFound);
157 
158   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
159   signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(),
160                                     info.account_id, "GAIA_IMAGE_URL_WITH_SIZE",
161                                     gaia_picture);
162   // Set a fake picture URL.
163   EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon()));
164   // Log out.
165   identity_test_env()->ClearPrimaryAccount();
166   // Verify that the GAIA name and picture, and picture URL are unset.
167   EXPECT_TRUE(entry->GetGAIAGivenName().empty());
168   EXPECT_TRUE(entry->GetGAIAName().empty());
169   EXPECT_EQ(nullptr, entry->GetGAIAPicture());
170   EXPECT_TRUE(entry->GetHostedDomain().empty());
171   EXPECT_TRUE(profile()
172                   ->GetPrefs()
173                   ->GetString(prefs::kGoogleServicesHostedDomain)
174                   .empty());
175 }
176 
177 #if !defined(ANDROID)
TEST_F(GAIAInfoUpdateServiceTest,SyncOnSyncOffKeepAllAccounts)178 TEST_F(GAIAInfoUpdateServiceTest, SyncOnSyncOffKeepAllAccounts) {
179   AccountInfo info =
180       identity_test_env()->MakeAccountAvailable("pat@example.com");
181   base::RunLoop().RunUntilIdle();
182   identity_test_env()->SetPrimaryAccount(info.email);
183   info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo",
184                              kNoHostedDomainFound);
185   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
186                                       info);
187   base::RunLoop().RunUntilIdle();
188 
189   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
190   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
191   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
192   signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(),
193                                     info.account_id, "GAIA_IMAGE_URL_WITH_SIZE",
194                                     gaia_picture);
195   // Turn off sync but stay logged in.
196   identity_test_env()->ClearPrimaryAccount(
197       signin::ClearPrimaryAccountPolicy::KEEP_ALL_ACCOUNTS);
198   ASSERT_TRUE(identity_test_env()->identity_manager()->HasPrimaryAccount(
199       signin::ConsentLevel::kNotRequired));
200   // Verify that the GAIA name and picture, and picture URL are not cleared
201   // as unconsented primary account still exists.
202   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
203   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
204   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
205   EXPECT_EQ(
206       profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
207       kNoHostedDomainFound);
208   EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon()));
209 }
210 
TEST_F(GAIAInfoUpdateServiceTest,LogInLogOut)211 TEST_F(GAIAInfoUpdateServiceTest, LogInLogOut) {
212   std::string email = "pat@example.com";
213   AccountInfo info =
214       identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(email);
215   EXPECT_TRUE(identity_test_env()->identity_manager()->HasPrimaryAccount(
216       signin::ConsentLevel::kNotRequired));
217   EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount());
218   info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo",
219                              kNoHostedDomainFound);
220   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
221                                       info);
222   base::RunLoop().RunUntilIdle();
223 
224   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
225   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
226   EXPECT_EQ(entry->GetGAIAGivenName(), base::UTF8ToUTF16("Pat"));
227   EXPECT_EQ(entry->GetGAIAName(), base::UTF8ToUTF16("Pat Foo"));
228   EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound);
229   EXPECT_EQ(
230       profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain),
231       kNoHostedDomainFound);
232 
233   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
234   signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(),
235                                     info.account_id, "GAIA_IMAGE_URL_WITH_SIZE",
236                                     gaia_picture);
237   // Set a fake picture URL.
238   EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon()));
239   // Log out.
240   identity_test_env()->ClearPrimaryAccount();
241   base::RunLoop().RunUntilIdle();
242 
243   // Verify that the GAIA name and picture, and picture URL are unset.
244   EXPECT_TRUE(entry->GetGAIAGivenName().empty());
245   EXPECT_TRUE(entry->GetGAIAName().empty());
246   EXPECT_EQ(nullptr, entry->GetGAIAPicture());
247   EXPECT_TRUE(entry->GetHostedDomain().empty());
248   EXPECT_TRUE(profile()
249                   ->GetPrefs()
250                   ->GetString(prefs::kGoogleServicesHostedDomain)
251                   .empty());
252 }
253 
TEST_F(GAIAInfoUpdateServiceTest,LogInLogOutLogIn)254 TEST_F(GAIAInfoUpdateServiceTest, LogInLogOutLogIn) {
255   std::string email1 = "pat1@example.com";
256   AccountInfo info1 = identity_test_env()->MakeAccountAvailableWithCookies(
257       email1, signin::GetTestGaiaIdForEmail(email1));
258   base::RunLoop().RunUntilIdle();
259   info1 = GetValidAccountInfo(info1.email, info1.account_id, "Pat 1",
260                               "Pat Foo The First", kNoHostedDomainFound);
261   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
262                                       info1);
263   base::RunLoop().RunUntilIdle();
264   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
265   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
266 
267   // Test correct histogram recording for all accounts info that has no getters.
268   base::HistogramTester tester;
269   entry->RecordAccountMetrics();
270   tester.ExpectBucketCount(
271       "Profile.AllAccounts.Names",
272       /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName,
273       /*expected_count=*/1);
274   tester.ExpectBucketCount(
275       "Profile.AllAccounts.Categories",
276       /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory,
277       /*expected_count=*/1);
278 
279   // Log out and record the metric again, sign-out wipes previous info in the
280   // entry so again the default values get reported.
281   identity_test_env()->SetCookieAccounts({});
282   entry->RecordAccountMetrics();
283   tester.ExpectBucketCount(
284       "Profile.AllAccounts.Names",
285       /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName,
286       /*expected_count=*/2);
287   tester.ExpectBucketCount(
288       "Profile.AllAccounts.Categories",
289       /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory,
290       /*expected_count=*/2);
291 
292   std::string email2 = "pat2@example.com";
293   AccountInfo info2 = identity_test_env()->MakeAccountAvailableWithCookies(
294       email2, signin::GetTestGaiaIdForEmail(email2));
295   base::RunLoop().RunUntilIdle();
296   info2 = GetValidAccountInfo(info2.email, info2.account_id, "Pat 2",
297                               "Pat Foo The Second", kChromiumOrgDomain);
298   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
299                                       info2);
300   base::RunLoop().RunUntilIdle();
301   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
302 
303   // Because due to the complete sign-out, the info about the previous account
304   // got wiped. Thus the same default metrics get recorded again, despite the
305   // second account has a different gaia name and a different account category
306   // than the first one.
307   entry->RecordAccountMetrics();
308   tester.ExpectBucketCount(
309       "Profile.AllAccounts.Names",
310       /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName,
311       /*expected_count=*/3);
312   tester.ExpectBucketCount(
313       "Profile.AllAccounts.Categories",
314       /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory,
315       /*expected_count=*/3);
316   tester.ExpectTotalCount("Profile.AllAccounts.Names", /*expected_count=*/3);
317   tester.ExpectTotalCount("Profile.AllAccounts.Categories",
318                           /*expected_count=*/3);
319 }
320 
TEST_F(GAIAInfoUpdateServiceTest,MultiLoginAndLogOut)321 TEST_F(GAIAInfoUpdateServiceTest, MultiLoginAndLogOut) {
322   // Make two accounts available with both refresh token and cookies.
323   AccountInfo info1 =
324       identity_test_env()->MakeAccountAvailable("pat@example.com");
325   AccountInfo info2 =
326       identity_test_env()->MakeAccountAvailable("pat2@example.com");
327   identity_test_env()->SetCookieAccounts(
328       {{info1.email, info1.gaia}, {info2.email, info2.gaia}});
329   base::RunLoop().RunUntilIdle();
330   info1 = GetValidAccountInfo(info1.email, info1.account_id, "Pat 1",
331                               "Pat Foo The First", kNoHostedDomainFound);
332   // Make the second account an enterprise account by setting a hosted domain.
333   info2 = GetValidAccountInfo(info2.email, info2.account_id, "Pat 2",
334                               "Pat Foo The Second", kChromiumOrgDomain);
335   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
336                                       info1);
337   signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(),
338                                       info2);
339   base::RunLoop().RunUntilIdle();
340   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
341   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
342 
343   // Test correct histogram recording for all accounts info that has no getters.
344   // The two accounts have both different gaia names and account categories.
345   base::HistogramTester tester;
346   entry->RecordAccountMetrics();
347   tester.ExpectBucketCount(
348       "Profile.AllAccounts.Names",
349       /*sample=*/profile_metrics::AllAccountsNames::kMultipleNamesWithoutSync,
350       /*expected_count=*/1);
351   tester.ExpectBucketCount(
352       "Profile.AllAccounts.Categories",
353       /*sample=*/
354       profile_metrics::AllAccountsCategories::kBothConsumerAndEnterpriseNoSync,
355       /*expected_count=*/1);
356 
357   // Log out and record the metric again, sign-out wipes previous info in the
358   // entry so the default values get reported.
359   identity_test_env()->SetCookieAccounts({});
360   entry->RecordAccountMetrics();
361   tester.ExpectBucketCount(
362       "Profile.AllAccounts.Names",
363       /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName,
364       /*expected_count=*/1);
365   tester.ExpectBucketCount(
366       "Profile.AllAccounts.Categories",
367       /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory,
368       /*expected_count=*/1);
369   tester.ExpectTotalCount("Profile.AllAccounts.Names", /*expected_count=*/2);
370   tester.ExpectTotalCount("Profile.AllAccounts.Categories",
371                           /*expected_count=*/2);
372 }
373 #endif  // !defined(ANDROID)
374 
TEST_F(GAIAInfoUpdateServiceTest,ClearGaiaInfoOnStartup)375 TEST_F(GAIAInfoUpdateServiceTest, ClearGaiaInfoOnStartup) {
376   // Simulate a state where the profile entry has GAIA related information
377   // when there is not primary account set.
378   ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
379       signin::ConsentLevel::kNotRequired));
380   ASSERT_EQ(1u, storage()->GetNumberOfProfiles());
381   ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front();
382   entry->SetGAIAName(base::UTF8ToUTF16("foo"));
383   entry->SetGAIAGivenName(base::UTF8ToUTF16("Pat Foo"));
384   gfx::Image gaia_picture = gfx::test::CreateImage(256, 256);
385   entry->SetGAIAPicture("GAIA_IMAGE_URL_WITH_SIZE", gaia_picture);
386   entry->SetHostedDomain(kNoHostedDomainFound);
387 
388   // Verify that creating the GAIAInfoUpdateService resets the GAIA related
389   // profile attributes if the profile no longer has a primary account and that
390   // the profile info cache observer wass notified about profile name and
391   // avatar changes.
392   RecreateGAIAInfoUpdateService();
393 
394   EXPECT_TRUE(entry->GetGAIAName().empty());
395   EXPECT_TRUE(entry->GetGAIAGivenName().empty());
396   EXPECT_FALSE(entry->GetGAIAPicture());
397   EXPECT_TRUE(entry->GetHostedDomain().empty());
398 }
399 
400 #endif  // !defined(OS_CHROMEOS)
401