1 // Copyright 2019 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/ui/webui/settings/chromeos/account_manager_handler.h"
6 
7 #include <memory>
8 #include <ostream>
9 
10 #include "base/test/bind_test_util.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/browser_process_platform_part.h"
13 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
14 #include "chrome/browser/chromeos/profiles/profile_helper.h"
15 #include "chrome/browser/signin/identity_manager_factory.h"
16 #include "chrome/test/base/in_process_browser_test.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "chromeos/components/account_manager/account_manager.h"
19 #include "chromeos/components/account_manager/account_manager_factory.h"
20 #include "components/signin/public/identity_manager/identity_manager.h"
21 #include "components/signin/public/identity_manager/identity_test_utils.h"
22 #include "components/user_manager/scoped_user_manager.h"
23 #include "components/user_manager/user_type.h"
24 #include "content/public/test/test_web_ui.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 namespace {
28 constexpr char kGetAccountsMessage[] = "getAccounts";
29 constexpr char kHandleFunctionName[] = "handleFunctionName";
30 
31 struct DeviceAccountInfo {
32   std::string id;
33   std::string email;
34   std::string fullName;
35   std::string organization;
36 
37   user_manager::UserType user_type;
38   chromeos::account_manager::AccountType account_type;
39   std::string token;
40 
41   friend std::ostream& operator<<(std::ostream& stream,
42                                   const DeviceAccountInfo& device_account_info);
43 };
44 
operator <<(std::ostream & stream,const DeviceAccountInfo & device_account_info)45 std::ostream& operator<<(std::ostream& stream,
46                          const DeviceAccountInfo& device_account_info) {
47   return stream << "{email: " << device_account_info.email
48                 << ", user_type: " << device_account_info.user_type << "}";
49 }
50 
GetActiveDirectoryDeviceAccountInfo()51 DeviceAccountInfo GetActiveDirectoryDeviceAccountInfo() {
52   return {"fake-ad-id" /*id*/,
53           "primary@example.com" /*email*/,
54           "primary" /*fullName*/,
55           "example.com" /*organization*/,
56           user_manager::USER_TYPE_ACTIVE_DIRECTORY /*user_type*/,
57           chromeos::account_manager::AccountType::
58               ACCOUNT_TYPE_ACTIVE_DIRECTORY /*account_type*/,
59           chromeos::AccountManager::kActiveDirectoryDummyToken /*token*/};
60 }
61 
GetGaiaDeviceAccountInfo()62 DeviceAccountInfo GetGaiaDeviceAccountInfo() {
63   return {signin::GetTestGaiaIdForEmail("primary@example.com") /*id*/,
64           "primary@example.com" /*email*/,
65           "primary" /*fullName*/,
66           "" /*organization*/,
67           user_manager::USER_TYPE_REGULAR /*user_type*/,
68           chromeos::account_manager::AccountType::
69               ACCOUNT_TYPE_GAIA /*account_type*/,
70           "device-account-token" /*token*/};
71 }
72 
GetAccountByKey(std::vector<chromeos::AccountManager::Account> accounts,chromeos::AccountManager::AccountKey key)73 chromeos::AccountManager::Account GetAccountByKey(
74     std::vector<chromeos::AccountManager::Account> accounts,
75     chromeos::AccountManager::AccountKey key) {
76   for (const chromeos::AccountManager::Account& account : accounts) {
77     if (account.key == key) {
78       return account;
79     }
80   }
81   return chromeos::AccountManager::Account();
82 }
83 
ValueOrEmpty(const std::string * str)84 std::string ValueOrEmpty(const std::string* str) {
85   return str ? *str : std::string();
86 }
87 
88 }  // namespace
89 
90 namespace chromeos {
91 namespace settings {
92 
93 class TestingAccountManagerUIHandler : public AccountManagerUIHandler {
94  public:
TestingAccountManagerUIHandler(AccountManager * account_manager,signin::IdentityManager * identity_manager,content::WebUI * web_ui)95   TestingAccountManagerUIHandler(AccountManager* account_manager,
96                                  signin::IdentityManager* identity_manager,
97                                  content::WebUI* web_ui)
98       : AccountManagerUIHandler(account_manager, identity_manager) {
99     set_web_ui(web_ui);
100   }
101 
102  private:
103   DISALLOW_COPY_AND_ASSIGN(TestingAccountManagerUIHandler);
104 };
105 
106 class AccountManagerUIHandlerTest
107     : public InProcessBrowserTest,
108       public testing::WithParamInterface<DeviceAccountInfo> {
109  public:
110   AccountManagerUIHandlerTest() = default;
111   AccountManagerUIHandlerTest(const AccountManagerUIHandlerTest&) = delete;
112   AccountManagerUIHandlerTest& operator=(const AccountManagerUIHandlerTest&) =
113       delete;
114 
SetUpOnMainThread()115   void SetUpOnMainThread() override {
116     user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
117         std::make_unique<chromeos::FakeChromeUserManager>());
118     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
119     TestingProfile::Builder profile_builder;
120     profile_builder.SetPath(temp_dir_.GetPath().AppendASCII("TestProfile"));
121     profile_builder.SetProfileName(GetDeviceAccountInfo().email);
122     profile_ = profile_builder.Build();
123 
124     const user_manager::User* user;
125     if (GetDeviceAccountInfo().user_type ==
126         user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY) {
127       user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
128           AccountId::AdFromUserEmailObjGuid(GetDeviceAccountInfo().email,
129                                             GetDeviceAccountInfo().id),
130           true, user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
131           profile_.get());
132     } else {
133       user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
134           AccountId::FromUserEmailGaiaId(GetDeviceAccountInfo().email,
135                                          GetDeviceAccountInfo().id),
136           true, GetDeviceAccountInfo().user_type, profile_.get());
137     }
138 
139     identity_manager_ = IdentityManagerFactory::GetForProfile(profile_.get());
140 
141     chromeos::AccountManagerFactory* factory =
142         g_browser_process->platform_part()->GetAccountManagerFactory();
143     account_manager_ = factory->GetAccountManager(profile_->GetPath().value());
144 
145     account_manager_->UpsertAccount(
146         AccountManager::AccountKey{GetDeviceAccountInfo().id,
147                                    GetDeviceAccountInfo().account_type},
148         GetDeviceAccountInfo().email, GetDeviceAccountInfo().token);
149 
150     handler_ = std::make_unique<TestingAccountManagerUIHandler>(
151         account_manager_, identity_manager_, &web_ui_);
152     handler_->SetProfileForTesting(profile_.get());
153     handler_->RegisterMessages();
154     handler_->AllowJavascriptForTesting();
155     base::RunLoop().RunUntilIdle();
156   }
157 
TearDownOnMainThread()158   void TearDownOnMainThread() override {
159     handler_.reset();
160     profile_.reset();
161     user_manager_enabler_.reset();
162   }
163 
UpsertAccount(std::string email)164   void UpsertAccount(std::string email) {
165     account_manager_->UpsertAccount(
166         AccountManager::AccountKey{
167             signin::GetTestGaiaIdForEmail(email),
168             chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA},
169         email, AccountManager::kInvalidToken);
170   }
171 
GetAccountsFromAccountManager() const172   std::vector<AccountManager::Account> GetAccountsFromAccountManager() const {
173     std::vector<AccountManager::Account> accounts;
174 
175     base::RunLoop run_loop;
176     account_manager_->GetAccounts(base::BindLambdaForTesting(
177         [&accounts, &run_loop](
178             const std::vector<AccountManager::Account>& stored_accounts) {
179           accounts = stored_accounts;
180           run_loop.Quit();
181         }));
182     run_loop.Run();
183 
184     return accounts;
185   }
186 
GetDeviceAccountInfo() const187   DeviceAccountInfo GetDeviceAccountInfo() const { return GetParam(); }
188 
web_ui()189   content::TestWebUI* web_ui() { return &web_ui_; }
identity_manager()190   signin::IdentityManager* identity_manager() { return identity_manager_; }
account_manager()191   chromeos::AccountManager* account_manager() { return account_manager_; }
192 
193  private:
GetFakeUserManager() const194   chromeos::FakeChromeUserManager* GetFakeUserManager() const {
195     return static_cast<chromeos::FakeChromeUserManager*>(
196         user_manager::UserManager::Get());
197   }
198 
199   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
200   base::ScopedTempDir temp_dir_;
201   std::unique_ptr<TestingProfile> profile_;
202   chromeos::AccountManager* account_manager_ = nullptr;
203   signin::IdentityManager* identity_manager_ = nullptr;
204   content::TestWebUI web_ui_;
205   std::unique_ptr<TestingAccountManagerUIHandler> handler_;
206 };
207 
IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,OnGetAccountsNoSecondaryAccounts)208 IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
209                        OnGetAccountsNoSecondaryAccounts) {
210   const std::vector<AccountManager::Account> account_manager_accounts =
211       GetAccountsFromAccountManager();
212   // Only Primary account.
213   ASSERT_EQ(1UL, account_manager_accounts.size());
214 
215   // Call "getAccounts".
216   base::ListValue args;
217   args.AppendString(kHandleFunctionName);
218   web_ui()->HandleReceivedMessage(kGetAccountsMessage, &args);
219 
220   const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
221   EXPECT_EQ("cr.webUIResponse", call_data.function_name());
222   EXPECT_EQ(kHandleFunctionName, call_data.arg1()->GetString());
223   ASSERT_TRUE(call_data.arg2()->GetBool());
224 
225   // Get results from JS callback.
226   const base::span<const base::Value> result = call_data.arg3()->GetList();
227   ASSERT_EQ(account_manager_accounts.size(), result.size());
228 
229   // Check first (device) account.
230   const base::Value& device_account = result[0];
231   EXPECT_TRUE(device_account.FindBoolKey("isDeviceAccount").value());
232   EXPECT_TRUE(device_account.FindBoolKey("isSignedIn").value());
233   EXPECT_FALSE(device_account.FindBoolKey("unmigrated").value());
234   EXPECT_EQ(GetDeviceAccountInfo().account_type,
235             device_account.FindIntKey("accountType"));
236   EXPECT_EQ(GetDeviceAccountInfo().email,
237             ValueOrEmpty(device_account.FindStringKey("email")));
238   EXPECT_EQ(GetDeviceAccountInfo().id,
239             ValueOrEmpty(device_account.FindStringKey("id")));
240   EXPECT_EQ(GetDeviceAccountInfo().organization,
241             ValueOrEmpty(device_account.FindStringKey("organization")));
242 }
243 
IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,OnGetAccountsWithSecondaryAccounts)244 IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
245                        OnGetAccountsWithSecondaryAccounts) {
246   UpsertAccount("secondary1@example.com");
247   UpsertAccount("secondary2@example.com");
248   const std::vector<AccountManager::Account> account_manager_accounts =
249       GetAccountsFromAccountManager();
250   ASSERT_EQ(3UL, account_manager_accounts.size());
251 
252   // Call "getAccounts".
253   base::ListValue args;
254   args.AppendString(kHandleFunctionName);
255   web_ui()->HandleReceivedMessage(kGetAccountsMessage, &args);
256 
257   const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
258   EXPECT_EQ("cr.webUIResponse", call_data.function_name());
259   EXPECT_EQ(kHandleFunctionName, call_data.arg1()->GetString());
260   ASSERT_TRUE(call_data.arg2()->GetBool());
261 
262   // Get results from JS callback.
263   const base::span<const base::Value> result = call_data.arg3()->GetList();
264   ASSERT_EQ(account_manager_accounts.size(), result.size());
265 
266   // Check first (device) account.
267   const base::Value& device_account = result[0];
268   EXPECT_TRUE(device_account.FindBoolKey("isDeviceAccount").value());
269   EXPECT_TRUE(device_account.FindBoolKey("isSignedIn").value());
270   EXPECT_FALSE(device_account.FindBoolKey("unmigrated").value());
271   EXPECT_EQ(GetDeviceAccountInfo().account_type,
272             device_account.FindIntKey("accountType"));
273   EXPECT_EQ(GetDeviceAccountInfo().email,
274             ValueOrEmpty(device_account.FindStringKey("email")));
275   EXPECT_EQ(GetDeviceAccountInfo().id,
276             ValueOrEmpty(device_account.FindStringKey("id")));
277   EXPECT_EQ(GetDeviceAccountInfo().organization,
278             ValueOrEmpty(device_account.FindStringKey("organization")));
279 
280   // Check secondary accounts.
281   for (const base::Value& account : result) {
282     if (ValueOrEmpty(account.FindStringKey("id")) == GetDeviceAccountInfo().id)
283       continue;
284     EXPECT_FALSE(account.FindBoolKey("isDeviceAccount").value());
285 
286     AccountManager::Account expected_account =
287         GetAccountByKey(account_manager_accounts,
288                         {ValueOrEmpty(account.FindStringKey("id")),
289                          account_manager::AccountType::ACCOUNT_TYPE_GAIA});
290     EXPECT_EQ(account_manager()->HasDummyGaiaToken(expected_account.key),
291               account.FindBoolKey("unmigrated").value());
292     EXPECT_EQ(expected_account.key.account_type,
293               account.FindIntKey("accountType"));
294     EXPECT_EQ(expected_account.raw_email,
295               ValueOrEmpty(account.FindStringKey("email")));
296 
297     base::Optional<AccountInfo> expected_account_info =
298         identity_manager()
299             ->FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId(
300                 expected_account.key.id);
301     EXPECT_TRUE(expected_account_info.has_value());
302     EXPECT_EQ(expected_account_info->full_name,
303               ValueOrEmpty(account.FindStringKey("fullName")));
304     EXPECT_EQ(
305         !identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
306             expected_account_info->account_id),
307         account.FindBoolKey("isSignedIn").value());
308   }
309 }
310 
311 INSTANTIATE_TEST_SUITE_P(
312     AccountManagerUIHandlerTestSuite,
313     AccountManagerUIHandlerTest,
314     ::testing::Values(GetActiveDirectoryDeviceAccountInfo(),
315                       GetGaiaDeviceAccountInfo()));
316 
317 }  // namespace settings
318 }  // namespace chromeos
319