1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/login/supervised/supervised_user_test_base.h"
9 #include "base/compiler_specific.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/login/login_manager_test.h"
17 #include "chrome/browser/chromeos/login/startup_utils.h"
18 #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
19 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
20 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
21 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
22 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
23 #include "chrome/browser/chromeos/login/users/supervised_user_manager_impl.h"
24 #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h"
25 #include "chrome/browser/chromeos/profiles/profile_helper.h"
26 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
27 #include "chrome/browser/profiles/profile_impl.h"
28 #include "chrome/browser/supervised_user/legacy/supervised_user_registration_utility.h"
29 #include "chrome/browser/supervised_user/legacy/supervised_user_registration_utility_stub.h"
30 #include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service.h"
31 #include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service_factory.h"
32 #include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
33 #include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
34 #include "chrome/browser/supervised_user/supervised_user_constants.h"
35 #include "chromeos/cryptohome/mock_async_method_caller.h"
36 #include "chromeos/cryptohome/mock_homedir_methods.h"
37 #include "chromeos/login/auth/key.h"
38 #include "chromeos/login/auth/user_context.h"
39 #include "content/public/browser/notification_service.h"
40 #include "content/public/test/browser_test_utils.h"
41 #include "content/public/test/test_utils.h"
42 #include "sync/api/fake_sync_change_processor.h"
43 #include "sync/api/sync_change.h"
44 #include "sync/api/sync_error_factory_mock.h"
45 #include "sync/internal_api/public/attachments/attachment_service_proxy_for_test.h"
46 #include "sync/protocol/sync.pb.h"
49 using base::StringPrintf
;
55 const char kCurrentPage
[] = "$('supervised-user-creation').currentPage_";
57 const char kStubEthernetGuid
[] = "eth0";
61 SupervisedUsersSyncTestAdapter::SupervisedUsersSyncTestAdapter(Profile
* profile
)
62 : processor_(), next_sync_data_id_(0) {
63 service_
= SupervisedUserSyncServiceFactory::GetForProfile(profile
);
64 processor_
= new syncer::FakeSyncChangeProcessor();
65 service_
->MergeDataAndStartSyncing(
66 syncer::SUPERVISED_USERS
,
67 syncer::SyncDataList(),
68 scoped_ptr
<syncer::SyncChangeProcessor
>(processor_
),
69 scoped_ptr
<syncer::SyncErrorFactory
>(new syncer::SyncErrorFactoryMock
));
72 scoped_ptr
< ::sync_pb::ManagedUserSpecifics
>
73 SupervisedUsersSyncTestAdapter::GetFirstChange() {
74 scoped_ptr
< ::sync_pb::ManagedUserSpecifics
> result(
75 new ::sync_pb::ManagedUserSpecifics
);
77 << "GetFirstChange() should only be callled if HasChanges() is true";
78 const syncer::SyncData
& data
= processor_
->changes().front().sync_data();
79 EXPECT_EQ(syncer::SUPERVISED_USERS
, data
.GetDataType());
80 result
->CopyFrom(data
.GetSpecifics().managed_user());
84 void SupervisedUsersSyncTestAdapter::AddChange(
85 const ::sync_pb::ManagedUserSpecifics
& proto
,
87 sync_pb::EntitySpecifics specifics
;
89 specifics
.mutable_managed_user()->CopyFrom(proto
);
91 syncer::SyncData change_data
= syncer::SyncData::CreateRemoteData(
95 syncer::AttachmentIdList(),
96 syncer::AttachmentServiceProxyForTest::Create());
97 syncer::SyncChange
change(FROM_HERE
,
98 update
? syncer::SyncChange::ACTION_UPDATE
99 : syncer::SyncChange::ACTION_ADD
,
102 syncer::SyncChangeList change_list
;
103 change_list
.push_back(change
);
105 service_
->ProcessSyncChanges(FROM_HERE
, change_list
);
108 SupervisedUsersSharedSettingsSyncTestAdapter::
109 SupervisedUsersSharedSettingsSyncTestAdapter(Profile
* profile
)
110 : processor_(), next_sync_data_id_(0) {
112 SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile
);
113 processor_
= new syncer::FakeSyncChangeProcessor();
114 service_
->MergeDataAndStartSyncing(
115 syncer::SUPERVISED_USER_SHARED_SETTINGS
,
116 syncer::SyncDataList(),
117 scoped_ptr
<syncer::SyncChangeProcessor
>(processor_
),
118 scoped_ptr
<syncer::SyncErrorFactory
>(new syncer::SyncErrorFactoryMock
));
121 scoped_ptr
< ::sync_pb::ManagedUserSharedSettingSpecifics
>
122 SupervisedUsersSharedSettingsSyncTestAdapter::GetFirstChange() {
123 scoped_ptr
< ::sync_pb::ManagedUserSharedSettingSpecifics
> result(
124 new ::sync_pb::ManagedUserSharedSettingSpecifics
);
126 << "GetFirstChange() should only be callled if HasChanges() is true";
127 const syncer::SyncData
& data
= processor_
->changes().front().sync_data();
128 EXPECT_EQ(syncer::SUPERVISED_USER_SHARED_SETTINGS
, data
.GetDataType());
129 result
->CopyFrom(data
.GetSpecifics().managed_user_shared_setting());
130 return result
.Pass();
133 void SupervisedUsersSharedSettingsSyncTestAdapter::AddChange(
134 const ::sync_pb::ManagedUserSharedSettingSpecifics
& proto
,
136 sync_pb::EntitySpecifics specifics
;
138 specifics
.mutable_managed_user_shared_setting()->CopyFrom(proto
);
140 syncer::SyncData change_data
= syncer::SyncData::CreateRemoteData(
141 ++next_sync_data_id_
,
144 syncer::AttachmentIdList(),
145 syncer::AttachmentServiceProxyForTest::Create());
146 syncer::SyncChange
change(FROM_HERE
,
147 update
? syncer::SyncChange::ACTION_UPDATE
148 : syncer::SyncChange::ACTION_ADD
,
151 syncer::SyncChangeList change_list
;
152 change_list
.push_back(change
);
154 service_
->ProcessSyncChanges(FROM_HERE
, change_list
);
157 void SupervisedUsersSharedSettingsSyncTestAdapter::AddChange(
158 const std::string
& mu_id
,
159 const std::string
& key
,
160 const base::Value
& value
,
163 syncer::SyncData data
=
164 SupervisedUserSharedSettingsService::CreateSyncDataForSetting(
165 mu_id
, key
, value
, acknowledged
);
166 AddChange(data
.GetSpecifics().managed_user_shared_setting(), update
);
169 SupervisedUserTestBase::SupervisedUserTestBase()
170 : LoginManagerTest(true),
171 mock_async_method_caller_(NULL
),
172 mock_homedir_methods_(NULL
),
173 network_portal_detector_(NULL
),
174 registration_utility_stub_(NULL
) {
177 SupervisedUserTestBase::~SupervisedUserTestBase() {
180 void SupervisedUserTestBase::SetUpInProcessBrowserTestFixture() {
181 LoginManagerTest::SetUpInProcessBrowserTestFixture();
182 mock_async_method_caller_
= new cryptohome::MockAsyncMethodCaller
;
183 mock_async_method_caller_
->SetUp(true, cryptohome::MOUNT_ERROR_NONE
);
184 cryptohome::AsyncMethodCaller::InitializeForTesting(
185 mock_async_method_caller_
);
187 mock_homedir_methods_
= new cryptohome::MockHomedirMethods
;
188 mock_homedir_methods_
->SetUp(true, cryptohome::MOUNT_ERROR_NONE
);
189 cryptohome::HomedirMethods::InitializeForTesting(mock_homedir_methods_
);
191 registration_utility_stub_
= new SupervisedUserRegistrationUtilityStub();
192 scoped_utility_
.reset(new ScopedTestingSupervisedUserRegistrationUtility(
193 registration_utility_stub_
));
195 // Setup network portal detector to return online state for both
196 // ethernet and wifi networks. Ethernet is an active network by
198 network_portal_detector_
= new NetworkPortalDetectorTestImpl();
199 NetworkPortalDetector::InitializeForTesting(network_portal_detector_
);
200 NetworkPortalDetector::CaptivePortalState online_state
;
201 online_state
.status
= NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE
;
202 online_state
.response_code
= 204;
203 network_portal_detector_
->SetDefaultNetworkForTesting(kStubEthernetGuid
);
204 network_portal_detector_
->SetDetectionResultsForTesting(kStubEthernetGuid
,
208 void SupervisedUserTestBase::TearDown() {
209 cryptohome::AsyncMethodCaller::Shutdown();
210 cryptohome::HomedirMethods::Shutdown();
211 mock_homedir_methods_
= NULL
;
212 mock_async_method_caller_
= NULL
;
213 LoginManagerTest::TearDown();
216 void SupervisedUserTestBase::TearDownInProcessBrowserTestFixture() {
217 NetworkPortalDetector::Shutdown();
220 void SupervisedUserTestBase::JSEval(const std::string
& script
) {
221 EXPECT_TRUE(content::ExecuteScript(web_contents(), script
)) << script
;
224 void SupervisedUserTestBase::JSEvalOrExitBrowser(const std::string
& script
) {
225 ignore_result(content::ExecuteScript(web_contents(), script
));
228 void SupervisedUserTestBase::JSExpectAsync(const std::string
& function
) {
230 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
233 "(%s)(function() { window.domAutomationController.send(true); });",
235 &result
)) << function
;
239 void SupervisedUserTestBase::JSSetTextField(const std::string
& element_selector
,
240 const std::string
& value
) {
241 std::string function
=
242 StringPrintf("document.querySelector('%s').value = '%s'",
243 element_selector
.c_str(),
248 void SupervisedUserTestBase::PrepareUsers() {
249 RegisterUser(kTestManager
);
250 RegisterUser(kTestOtherUser
);
251 chromeos::StartupUtils::MarkOobeCompleted();
254 void SupervisedUserTestBase::StartFlowLoginAsManager() {
255 // Navigate to supervised user creation screen.
256 JSEval("chrome.send('showSupervisedUserCreationScreen')");
258 // Read intro and proceed.
259 JSExpect(StringPrintf("%s == 'intro'", kCurrentPage
));
261 JSEval("$('supervised-user-creation-start-button').click()");
263 // Check that both users appear as managers, and test-manager@gmail.com is
265 JSExpect(StringPrintf("%s == 'manager'", kCurrentPage
));
267 std::string manager_pods
=
268 "document.querySelectorAll('#supervised-user-creation-managers-pane "
270 std::string selected_manager_pods
=
271 "document.querySelectorAll('#supervised-user-creation-managers-pane "
272 ".manager-pod.focused')";
274 int managers_on_device
= 2;
276 JSExpect(StringPrintf("%s.length == 1", selected_manager_pods
.c_str()));
278 JSExpect(StringPrintf(
279 "$('supervised-user-creation').managerList_.pods.length == %d",
280 managers_on_device
));
281 JSExpect(StringPrintf(
282 "%s.length == %d", manager_pods
.c_str(), managers_on_device
));
283 JSExpect(StringPrintf("%s[%d].user.emailAddress == '%s'",
284 manager_pods
.c_str(),
288 // Select the first user as manager, and enter password.
289 JSExpect("$('supervised-user-creation-next-button').disabled");
290 JSSetTextField("#supervised-user-creation .manager-pod.focused input",
291 kTestManagerPassword
);
293 JSEval("$('supervised-user-creation').updateNextButtonForManager_()");
295 // Next button is now enabled.
296 JSExpect("!$('supervised-user-creation-next-button').disabled");
297 UserContext
user_context(kTestManager
);
298 user_context
.SetGaiaID(GetGaiaIDForUserID(kTestManager
));
299 user_context
.SetKey(Key(kTestManagerPassword
));
300 SetExpectedCredentials(user_context
);
301 content::WindowedNotificationObserver
login_observer(
302 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED
,
303 content::NotificationService::AllSources());
305 // Log in as manager.
306 JSEval("$('supervised-user-creation-next-button').click()");
307 login_observer
.Wait();
309 // OAuth token is valid.
310 user_manager::UserManager::Get()->SaveUserOAuthStatus(
311 kTestManager
, user_manager::User::OAUTH2_TOKEN_STATUS_VALID
);
312 base::RunLoop().RunUntilIdle();
314 // Check the page have changed.
315 JSExpect(StringPrintf("%s == 'username'", kCurrentPage
));
318 void SupervisedUserTestBase::FillNewUserData(const std::string
& display_name
) {
319 JSExpect("$('supervised-user-creation-next-button').disabled");
320 JSSetTextField("#supervised-user-creation-name", display_name
);
321 JSEval("$('supervised-user-creation').checkUserName_()");
323 base::RunLoop().RunUntilIdle();
325 JSSetTextField("#supervised-user-creation-password",
326 kTestSupervisedUserPassword
);
327 JSSetTextField("#supervised-user-creation-password-confirm",
328 kTestSupervisedUserPassword
);
330 JSEval("$('supervised-user-creation').updateNextButtonForUser_()");
331 JSExpect("!$('supervised-user-creation-next-button').disabled");
334 void SupervisedUserTestBase::StartUserCreation(
335 const std::string
& button_id
,
336 const std::string
& expected_display_name
) {
337 EXPECT_CALL(*mock_homedir_methods_
, MountEx(_
, _
, _
, _
)).Times(1);
338 EXPECT_CALL(*mock_homedir_methods_
, AddKeyEx(_
, _
, _
, _
, _
)).Times(1);
340 JSEval(std::string("$('").append(button_id
).append("').click()"));
342 ::testing::Mock::VerifyAndClearExpectations(mock_homedir_methods_
);
344 EXPECT_TRUE(registration_utility_stub_
->register_was_called());
345 EXPECT_EQ(registration_utility_stub_
->display_name(),
346 base::UTF8ToUTF16(expected_display_name
));
348 registration_utility_stub_
->RunSuccessCallback("token");
350 // Token writing moves control to BlockingPool and back.
351 content::RunAllBlockingPoolTasksUntilIdle();
353 JSExpect(StringPrintf("%s == 'created'", kCurrentPage
));
354 JSEvalOrExitBrowser("$('supervised-user-creation-gotit-button').click()");
357 void SupervisedUserTestBase::SigninAsSupervisedUser(
358 bool check_homedir_calls
,
360 const std::string
& expected_display_name
) {
361 if (check_homedir_calls
)
362 EXPECT_CALL(*mock_homedir_methods_
, MountEx(_
, _
, _
, _
)).Times(1);
364 // Log in as supervised user, make sure that everything works.
365 ASSERT_EQ(3UL, user_manager::UserManager::Get()->GetUsers().size());
367 // Created supervised user have to be first in a list.
368 const user_manager::User
* user
=
369 user_manager::UserManager::Get()->GetUsers().at(user_index
);
370 ASSERT_EQ(base::UTF8ToUTF16(expected_display_name
), user
->display_name());
372 // Clean first run flag before logging in.
373 static_cast<SupervisedUserManagerImpl
*>(
374 ChromeUserManager::Get()->GetSupervisedUserManager())
375 ->CheckForFirstRun(user
->email());
377 LoginUser(user
->email());
378 if (check_homedir_calls
)
379 ::testing::Mock::VerifyAndClearExpectations(mock_homedir_methods_
);
380 Profile
* profile
= ProfileHelper::Get()->GetProfileByUserUnsafe(user
);
381 shared_settings_adapter_
.reset(
382 new SupervisedUsersSharedSettingsSyncTestAdapter(profile
));
384 // Check ChromeOS preference is initialized.
386 static_cast<ProfileImpl
*>(profile
)->chromeos_preferences_
);
389 void SupervisedUserTestBase::SigninAsManager(int user_index
) {
390 // Log in as supervised user, make sure that everything works.
391 ASSERT_EQ(3UL, user_manager::UserManager::Get()->GetUsers().size());
393 // Created supervised user have to be first in a list.
394 const user_manager::User
* user
=
395 user_manager::UserManager::Get()->GetUsers().at(user_index
);
396 LoginUser(user
->email());
397 Profile
* profile
= ProfileHelper::Get()->GetProfileByUserUnsafe(user
);
398 shared_settings_adapter_
.reset(
399 new SupervisedUsersSharedSettingsSyncTestAdapter(profile
));
400 supervised_users_adapter_
.reset(new SupervisedUsersSyncTestAdapter(profile
));
403 void SupervisedUserTestBase::RemoveSupervisedUser(
404 size_t original_user_count
,
406 const std::string
& expected_display_name
) {
407 // Remove supervised user.
408 ASSERT_EQ(original_user_count
,
409 user_manager::UserManager::Get()->GetUsers().size());
411 // Created supervised user have to be first in a list.
412 const user_manager::User
* user
=
413 user_manager::UserManager::Get()->GetUsers().at(user_index
);
414 ASSERT_EQ(base::UTF8ToUTF16(expected_display_name
), user
->display_name());
418 StringPrintf("!$('pod-row').pods[%d].isActionBoxMenuActive", user_index
));
420 "$('pod-row').pods[%d].querySelector('.action-box-button').click()",
423 StringPrintf("$('pod-row').pods[%d].isActionBoxMenuActive", user_index
));
425 // Select "Remove user" element.
426 JSExpect(StringPrintf(
427 "$('pod-row').pods[%d].actionBoxRemoveUserWarningElement.hidden",
430 "$('pod-row').pods[%d].querySelector('.action-box-menu-remove').click()",
432 JSExpect(StringPrintf(
433 "!$('pod-row').pods[%d].actionBoxRemoveUserWarningElement.hidden",
436 EXPECT_CALL(*mock_async_method_caller_
, AsyncRemove(_
, _
)).Times(1);
440 "$('pod-row').pods[%d].querySelector('.remove-warning-button').click()",
443 // Make sure there is no supervised user in list.
444 ASSERT_EQ(original_user_count
- 1,
445 user_manager::UserManager::Get()->GetUsers().size());
448 } // namespace chromeos