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. 4 5 #include "chrome/browser/chromeos/login/managed/managed_user_test_base.h" 6 7 #include <string> 8 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/auth/key.h" 17 #include "chrome/browser/chromeos/login/auth/user_context.h" 18 #include "chrome/browser/chromeos/login/login_manager_test.h" 19 #include "chrome/browser/chromeos/login/managed/supervised_user_authentication.h" 20 #include "chrome/browser/chromeos/login/startup_utils.h" 21 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h" 22 #include "chrome/browser/chromeos/login/ui/webui_login_view.h" 23 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" 24 #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" 25 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" 26 #include "chrome/browser/profiles/profile_impl.h" 27 #include "chrome/browser/supervised_user/supervised_user_constants.h" 28 #include "chrome/browser/supervised_user/supervised_user_registration_utility.h" 29 #include "chrome/browser/supervised_user/supervised_user_registration_utility_stub.h" 30 #include "chrome/browser/supervised_user/supervised_user_shared_settings_service.h" 31 #include "chrome/browser/supervised_user/supervised_user_shared_settings_service_factory.h" 32 #include "chrome/browser/supervised_user/supervised_user_sync_service.h" 33 #include "chrome/browser/supervised_user/supervised_user_sync_service_factory.h" 34 #include "chromeos/cryptohome/mock_async_method_caller.h" 35 #include "chromeos/cryptohome/mock_homedir_methods.h" 36 #include "content/public/browser/notification_service.h" 37 #include "content/public/test/browser_test_utils.h" 38 #include "content/public/test/test_utils.h" 39 #include "sync/api/attachments/attachment_service_proxy_for_test.h" 40 #include "sync/api/fake_sync_change_processor.h" 41 #include "sync/api/sync_change.h" 42 #include "sync/api/sync_error_factory_mock.h" 43 #include "sync/protocol/sync.pb.h" 44 45 using testing::_; 46 using base::StringPrintf; 47 48 namespace chromeos { 49 50 namespace { 51 52 const char kCurrentPage[] = "$('managed-user-creation').currentPage_"; 53 } 54 55 ManagedUsersSyncTestAdapter::ManagedUsersSyncTestAdapter(Profile* profile) 56 : processor_(), next_sync_data_id_(0) { 57 service_ = SupervisedUserSyncServiceFactory::GetForProfile(profile); 58 processor_ = new syncer::FakeSyncChangeProcessor(); 59 service_->MergeDataAndStartSyncing( 60 syncer::SUPERVISED_USERS, 61 syncer::SyncDataList(), 62 scoped_ptr<syncer::SyncChangeProcessor>(processor_), 63 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock)); 64 } 65 66 scoped_ptr< ::sync_pb::ManagedUserSpecifics> 67 ManagedUsersSyncTestAdapter::GetFirstChange() { 68 scoped_ptr< ::sync_pb::ManagedUserSpecifics> result( 69 new ::sync_pb::ManagedUserSpecifics); 70 CHECK(HasChanges()) 71 << "GetFirstChange() should only be callled if HasChanges() is true"; 72 const syncer::SyncData& data = processor_->changes().front().sync_data(); 73 EXPECT_EQ(syncer::SUPERVISED_USERS, data.GetDataType()); 74 result->CopyFrom(data.GetSpecifics().managed_user()); 75 return result.Pass(); 76 } 77 78 void ManagedUsersSyncTestAdapter::AddChange( 79 const ::sync_pb::ManagedUserSpecifics& proto, 80 bool update) { 81 sync_pb::EntitySpecifics specifics; 82 83 specifics.mutable_managed_user()->CopyFrom(proto); 84 85 syncer::SyncData change_data = syncer::SyncData::CreateRemoteData( 86 ++next_sync_data_id_, 87 specifics, 88 base::Time(), 89 syncer::AttachmentIdList(), 90 syncer::AttachmentServiceProxyForTest::Create()); 91 syncer::SyncChange change(FROM_HERE, 92 update ? syncer::SyncChange::ACTION_UPDATE 93 : syncer::SyncChange::ACTION_ADD, 94 change_data); 95 96 syncer::SyncChangeList change_list; 97 change_list.push_back(change); 98 99 service_->ProcessSyncChanges(FROM_HERE, change_list); 100 } 101 102 ManagedUsersSharedSettingsSyncTestAdapter:: 103 ManagedUsersSharedSettingsSyncTestAdapter(Profile* profile) 104 : processor_(), next_sync_data_id_(0) { 105 service_ = 106 SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile); 107 processor_ = new syncer::FakeSyncChangeProcessor(); 108 service_->MergeDataAndStartSyncing( 109 syncer::SUPERVISED_USER_SHARED_SETTINGS, 110 syncer::SyncDataList(), 111 scoped_ptr<syncer::SyncChangeProcessor>(processor_), 112 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock)); 113 } 114 115 scoped_ptr< ::sync_pb::ManagedUserSharedSettingSpecifics> 116 ManagedUsersSharedSettingsSyncTestAdapter::GetFirstChange() { 117 scoped_ptr< ::sync_pb::ManagedUserSharedSettingSpecifics> result( 118 new ::sync_pb::ManagedUserSharedSettingSpecifics); 119 CHECK(HasChanges()) 120 << "GetFirstChange() should only be callled if HasChanges() is true"; 121 const syncer::SyncData& data = processor_->changes().front().sync_data(); 122 EXPECT_EQ(syncer::SUPERVISED_USER_SHARED_SETTINGS, data.GetDataType()); 123 result->CopyFrom(data.GetSpecifics().managed_user_shared_setting()); 124 return result.Pass(); 125 } 126 127 void ManagedUsersSharedSettingsSyncTestAdapter::AddChange( 128 const ::sync_pb::ManagedUserSharedSettingSpecifics& proto, 129 bool update) { 130 sync_pb::EntitySpecifics specifics; 131 132 specifics.mutable_managed_user_shared_setting()->CopyFrom(proto); 133 134 syncer::SyncData change_data = syncer::SyncData::CreateRemoteData( 135 ++next_sync_data_id_, 136 specifics, 137 base::Time(), 138 syncer::AttachmentIdList(), 139 syncer::AttachmentServiceProxyForTest::Create()); 140 syncer::SyncChange change(FROM_HERE, 141 update ? syncer::SyncChange::ACTION_UPDATE 142 : syncer::SyncChange::ACTION_ADD, 143 change_data); 144 145 syncer::SyncChangeList change_list; 146 change_list.push_back(change); 147 148 service_->ProcessSyncChanges(FROM_HERE, change_list); 149 } 150 151 void ManagedUsersSharedSettingsSyncTestAdapter::AddChange( 152 const std::string& mu_id, 153 const std::string& key, 154 const base::Value& value, 155 bool acknowledged, 156 bool update) { 157 syncer::SyncData data = 158 SupervisedUserSharedSettingsService::CreateSyncDataForSetting( 159 mu_id, key, value, acknowledged); 160 AddChange(data.GetSpecifics().managed_user_shared_setting(), update); 161 } 162 163 ManagedUserTestBase::ManagedUserTestBase() 164 : LoginManagerTest(true), 165 mock_async_method_caller_(NULL), 166 mock_homedir_methods_(NULL), 167 network_portal_detector_(NULL), 168 registration_utility_stub_(NULL) { 169 } 170 171 ManagedUserTestBase::~ManagedUserTestBase() { 172 } 173 174 void ManagedUserTestBase::SetUpInProcessBrowserTestFixture() { 175 LoginManagerTest::SetUpInProcessBrowserTestFixture(); 176 mock_async_method_caller_ = new cryptohome::MockAsyncMethodCaller; 177 mock_async_method_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); 178 cryptohome::AsyncMethodCaller::InitializeForTesting( 179 mock_async_method_caller_); 180 181 mock_homedir_methods_ = new cryptohome::MockHomedirMethods; 182 mock_homedir_methods_->SetUp(true, cryptohome::MOUNT_ERROR_NONE); 183 cryptohome::HomedirMethods::InitializeForTesting(mock_homedir_methods_); 184 185 registration_utility_stub_ = new SupervisedUserRegistrationUtilityStub(); 186 scoped_utility_.reset(new ScopedTestingSupervisedUserRegistrationUtility( 187 registration_utility_stub_)); 188 189 // Setup network portal detector to return online state for both 190 // ethernet and wifi networks. Ethernet is an active network by 191 // default. 192 network_portal_detector_ = new NetworkPortalDetectorTestImpl(); 193 NetworkPortalDetector::InitializeForTesting(network_portal_detector_); 194 NetworkPortalDetector::CaptivePortalState online_state; 195 online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; 196 online_state.response_code = 204; 197 network_portal_detector_->SetDefaultNetworkPathForTesting( 198 kStubEthernetServicePath, 199 kStubEthernetServicePath /* guid */); 200 network_portal_detector_->SetDetectionResultsForTesting( 201 kStubEthernetServicePath, online_state); 202 } 203 204 void ManagedUserTestBase::CleanUpOnMainThread() { 205 LoginManagerTest::CleanUpOnMainThread(); 206 } 207 208 void ManagedUserTestBase::TearDown() { 209 cryptohome::AsyncMethodCaller::Shutdown(); 210 cryptohome::HomedirMethods::Shutdown(); 211 mock_homedir_methods_ = NULL; 212 mock_async_method_caller_ = NULL; 213 LoginManagerTest::TearDown(); 214 } 215 216 void ManagedUserTestBase::TearDownInProcessBrowserTestFixture() { 217 NetworkPortalDetector::Shutdown(); 218 } 219 220 void ManagedUserTestBase::JSEval(const std::string& script) { 221 EXPECT_TRUE(content::ExecuteScript(web_contents(), script)) << script; 222 } 223 224 void ManagedUserTestBase::JSExpectAsync(const std::string& function) { 225 bool result; 226 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( 227 web_contents(), 228 StringPrintf( 229 "(%s)(function() { window.domAutomationController.send(true); });", 230 function.c_str()), 231 &result)) << function; 232 EXPECT_TRUE(result); 233 } 234 235 void ManagedUserTestBase::JSSetTextField(const std::string& element_selector, 236 const std::string& value) { 237 std::string function = 238 StringPrintf("document.querySelector('%s').value = '%s'", 239 element_selector.c_str(), 240 value.c_str()); 241 JSEval(function); 242 } 243 244 void ManagedUserTestBase::PrepareUsers() { 245 RegisterUser(kTestManager); 246 RegisterUser(kTestOtherUser); 247 chromeos::StartupUtils::MarkOobeCompleted(); 248 } 249 250 void ManagedUserTestBase::StartFlowLoginAsManager() { 251 // Navigate to supervised user creation screen. 252 JSEval("chrome.send('showLocallyManagedUserCreationScreen')"); 253 254 // Read intro and proceed. 255 JSExpect(StringPrintf("%s == 'intro'", kCurrentPage)); 256 257 JSEval("$('managed-user-creation-start-button').click()"); 258 259 // Check that both users appear as managers, and test-manager (at) gmail.com is 260 // the first one. 261 JSExpect(StringPrintf("%s == 'manager'", kCurrentPage)); 262 263 std::string manager_pods = 264 "document.querySelectorAll('#managed-user-creation-managers-pane " 265 ".manager-pod')"; 266 std::string selected_manager_pods = 267 "document.querySelectorAll('#managed-user-creation-managers-pane " 268 ".manager-pod.focused')"; 269 270 int managers_on_device = 2; 271 272 JSExpect(StringPrintf("%s.length == 1", selected_manager_pods.c_str())); 273 274 JSExpect( 275 StringPrintf("$('managed-user-creation').managerList_.pods.length == %d", 276 managers_on_device)); 277 JSExpect(StringPrintf( 278 "%s.length == %d", manager_pods.c_str(), managers_on_device)); 279 JSExpect(StringPrintf("%s[%d].user.emailAddress == '%s'", 280 manager_pods.c_str(), 281 0, 282 kTestManager)); 283 284 // Select the first user as manager, and enter password. 285 JSExpect("$('managed-user-creation-next-button').disabled"); 286 JSSetTextField("#managed-user-creation .manager-pod.focused input", 287 kTestManagerPassword); 288 289 JSEval("$('managed-user-creation').updateNextButtonForManager_()"); 290 291 // Next button is now enabled. 292 JSExpect("!$('managed-user-creation-next-button').disabled"); 293 UserContext user_context(kTestManager); 294 user_context.SetKey(Key(kTestManagerPassword)); 295 SetExpectedCredentials(user_context); 296 content::WindowedNotificationObserver login_observer( 297 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, 298 content::NotificationService::AllSources()); 299 300 // Log in as manager. 301 JSEval("$('managed-user-creation-next-button').click()"); 302 login_observer.Wait(); 303 304 // OAuth token is valid. 305 UserManager::Get()->SaveUserOAuthStatus(kTestManager, 306 User::OAUTH2_TOKEN_STATUS_VALID); 307 base::RunLoop().RunUntilIdle(); 308 309 // Check the page have changed. 310 JSExpect(StringPrintf("%s == 'username'", kCurrentPage)); 311 } 312 313 void ManagedUserTestBase::FillNewUserData(const std::string& display_name) { 314 JSExpect("$('managed-user-creation-next-button').disabled"); 315 JSSetTextField("#managed-user-creation-name", display_name); 316 JSEval("$('managed-user-creation').checkUserName_()"); 317 318 base::RunLoop().RunUntilIdle(); 319 320 JSSetTextField("#managed-user-creation-password", 321 kTestSupervisedUserPassword); 322 JSSetTextField("#managed-user-creation-password-confirm", 323 kTestSupervisedUserPassword); 324 325 JSEval("$('managed-user-creation').updateNextButtonForUser_()"); 326 JSExpect("!$('managed-user-creation-next-button').disabled"); 327 } 328 329 void ManagedUserTestBase::StartUserCreation( 330 const std::string& button_id, 331 const std::string& expected_display_name) { 332 EXPECT_CALL(*mock_homedir_methods_, MountEx(_, _, _, _)).Times(1); 333 EXPECT_CALL(*mock_homedir_methods_, AddKeyEx(_, _, _, _, _)).Times(1); 334 335 JSEval(std::string("$('").append(button_id).append("').click()")); 336 337 ::testing::Mock::VerifyAndClearExpectations(mock_homedir_methods_); 338 339 EXPECT_TRUE(registration_utility_stub_->register_was_called()); 340 EXPECT_EQ(registration_utility_stub_->display_name(), 341 base::UTF8ToUTF16(expected_display_name)); 342 343 registration_utility_stub_->RunSuccessCallback("token"); 344 345 // Token writing moves control to BlockingPool and back. 346 base::RunLoop().RunUntilIdle(); 347 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 348 base::RunLoop().RunUntilIdle(); 349 350 JSExpect(StringPrintf("%s == 'created'", kCurrentPage)); 351 JSEval("$('managed-user-creation-gotit-button').click()"); 352 } 353 354 void ManagedUserTestBase::SigninAsSupervisedUser( 355 bool check_homedir_calls, 356 int user_index, 357 const std::string& expected_display_name) { 358 if (check_homedir_calls) 359 EXPECT_CALL(*mock_homedir_methods_, MountEx(_, _, _, _)).Times(1); 360 361 // Log in as supervised user, make sure that everything works. 362 ASSERT_EQ(3UL, UserManager::Get()->GetUsers().size()); 363 364 // Created supervised user have to be first in a list. 365 const User* user = UserManager::Get()->GetUsers().at(user_index); 366 ASSERT_EQ(base::UTF8ToUTF16(expected_display_name), user->display_name()); 367 LoginUser(user->email()); 368 if (check_homedir_calls) 369 ::testing::Mock::VerifyAndClearExpectations(mock_homedir_methods_); 370 Profile* profile = UserManager::Get()->GetProfileByUser(user); 371 shared_settings_adapter_.reset( 372 new ManagedUsersSharedSettingsSyncTestAdapter(profile)); 373 374 // Check ChromeOS preference is initialized. 375 EXPECT_TRUE( 376 static_cast<ProfileImpl*>(profile)->chromeos_preferences_); 377 } 378 379 void ManagedUserTestBase::SigninAsManager(int user_index) { 380 // Log in as supervised user, make sure that everything works. 381 ASSERT_EQ(3UL, UserManager::Get()->GetUsers().size()); 382 383 // Created supervised user have to be first in a list. 384 const User* user = UserManager::Get()->GetUsers().at(user_index); 385 LoginUser(user->email()); 386 Profile* profile = UserManager::Get()->GetProfileByUser(user); 387 shared_settings_adapter_.reset( 388 new ManagedUsersSharedSettingsSyncTestAdapter(profile)); 389 managed_users_adapter_.reset(new ManagedUsersSyncTestAdapter(profile)); 390 } 391 392 void ManagedUserTestBase::RemoveSupervisedUser( 393 unsigned long original_user_count, 394 int user_index, 395 const std::string& expected_display_name) { 396 // Remove supervised user. 397 ASSERT_EQ(original_user_count, UserManager::Get()->GetUsers().size()); 398 399 // Created supervised user have to be first in a list. 400 const User* user = UserManager::Get()->GetUsers().at(user_index); 401 ASSERT_EQ(base::UTF8ToUTF16(expected_display_name), user->display_name()); 402 403 // Open pod menu. 404 JSExpect( 405 StringPrintf("!$('pod-row').pods[%d].isActionBoxMenuActive", user_index)); 406 JSEval(StringPrintf( 407 "$('pod-row').pods[%d].querySelector('.action-box-button').click()", 408 user_index)); 409 JSExpect( 410 StringPrintf("$('pod-row').pods[%d].isActionBoxMenuActive", user_index)); 411 412 // Select "Remove user" element. 413 JSExpect(StringPrintf( 414 "$('pod-row').pods[%d].actionBoxRemoveUserWarningElement.hidden", 415 user_index)); 416 JSEval(StringPrintf( 417 "$('pod-row').pods[%d].querySelector('.action-box-menu-remove').click()", 418 user_index)); 419 JSExpect(StringPrintf( 420 "!$('pod-row').pods[%d].actionBoxRemoveUserWarningElement.hidden", 421 user_index)); 422 423 EXPECT_CALL(*mock_async_method_caller_, AsyncRemove(_, _)).Times(1); 424 425 // Confirm deletion. 426 JSEval(StringPrintf( 427 "$('pod-row').pods[%d].querySelector('.remove-warning-button').click()", 428 user_index)); 429 430 // Make sure there is no supervised user in list. 431 ASSERT_EQ(original_user_count - 1, UserManager::Get()->GetUsers().size()); 432 } 433 434 } // namespace chromeos 435