// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "apps/app_window.h" #include "apps/app_window_registry.h" #include "apps/ui/native_app_window.h" #include "ash/desktop_background/desktop_background_controller.h" #include "ash/desktop_background/desktop_background_controller_observer.h" #include "ash/shell.h" #include "base/file_util.h" #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/app_mode/fake_cws.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/login/app_launch_controller.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/test/app_window_waiter.h" #include "chrome/browser/chromeos/login/test/oobe_base_test.h" #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" #include "chrome/browser/chromeos/login/users/fake_user_manager.h" #include "chrome/browser/chromeos/login/users/mock_user_manager.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/profiles/profile_impl.h" #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" #include "components/signin/core/common/signin_pref_names.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/test/browser_test_utils.h" #include "extensions/browser/extension_system.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_switches.h" #include "google_apis/gaia/gaia_urls.h" #include "net/test/embedded_test_server/embedded_test_server.h" namespace em = enterprise_management; namespace chromeos { namespace { // This is a simple test app that creates an app window and immediately closes // it again. Webstore data json is in // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/ // detail/ggbflgnkafappblpkiflbgpmkfdpnhhe const char kTestKioskApp[] = "ggbflgnkafappblpkiflbgpmkfdpnhhe"; // This app creates a window and declares usage of the identity API in its // manifest, so we can test device robot token minting via the identity API. // Webstore data json is in // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/ // detail/ibjkkfdnfcaoapcpheeijckmpcfkifob const char kTestEnterpriseKioskApp[] = "ibjkkfdnfcaoapcpheeijckmpcfkifob"; // An offline enable test app. Webstore data json is in // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/ // detail/ajoggoflpgplnnjkjamcmbepjdjdnpdp // An app profile with version 1.0.0 installed is in // chrome/test/data/chromeos/app_mode/offline_enabled_app_profile // The version 2.0.0 crx is in // chrome/test/data/chromeos/app_mode/webstore/downloads/ const char kTestOfflineEnabledKioskApp[] = "ajoggoflpgplnnjkjamcmbepjdjdnpdp"; // An app to test local fs data persistence across app update. V1 app writes // data into local fs. V2 app reads and verifies the data. // Webstore data json is in // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/ // detail/bmbpicmpniaclbbpdkfglgipkkebnbjf const char kTestLocalFsKioskApp[] = "bmbpicmpniaclbbpdkfglgipkkebnbjf"; // Timeout while waiting for network connectivity during tests. const int kTestNetworkTimeoutSeconds = 1; // Email of owner account for test. const char kTestOwnerEmail[] = "owner@example.com"; const char kTestEnterpriseAccountId[] = "enterprise-kiosk-app@localhost"; const char kTestEnterpriseServiceAccountId[] = "service_account@example.com"; const char kTestRefreshToken[] = "fake-refresh-token"; const char kTestUserinfoToken[] = "fake-userinfo-token"; const char kTestLoginToken[] = "fake-login-token"; const char kTestAccessToken[] = "fake-access-token"; const char kTestClientId[] = "fake-client-id"; const char kTestAppScope[] = "https://www.googleapis.com/auth/userinfo.profile"; // Test JS API. const char kLaunchAppForTestNewAPI[] = "login.AccountPickerScreen.runAppForTesting"; const char kLaunchAppForTestOldAPI[] = "login.AppsMenuButton.runAppForTesting"; const char kCheckDiagnosticModeNewAPI[] = "$('oobe').confirmDiagnosticMode_"; const char kCheckDiagnosticModeOldAPI[] = "$('show-apps-button').confirmDiagnosticMode_"; // Helper function for GetConsumerKioskAutoLaunchStatusCallback. void ConsumerKioskAutoLaunchStatusCheck( KioskAppManager::ConsumerKioskAutoLaunchStatus* out_status, const base::Closure& runner_quit_task, KioskAppManager::ConsumerKioskAutoLaunchStatus in_status) { LOG(INFO) << "KioskAppManager::ConsumerKioskModeStatus = " << in_status; *out_status = in_status; runner_quit_task.Run(); } // Helper KioskAppManager::EnableKioskModeCallback implementation. void ConsumerKioskModeAutoStartLockCheck( bool* out_locked, const base::Closure& runner_quit_task, bool in_locked) { LOG(INFO) << "kiosk locked = " << in_locked; *out_locked = in_locked; runner_quit_task.Run(); } // Helper function for WaitForNetworkTimeOut. void OnNetworkWaitTimedOut(const base::Closure& runner_quit_task) { runner_quit_task.Run(); } // Helper functions for CanConfigureNetwork mock. class ScopedCanConfigureNetwork { public: ScopedCanConfigureNetwork(bool can_configure, bool needs_owner_auth) : can_configure_(can_configure), needs_owner_auth_(needs_owner_auth), can_configure_network_callback_( base::Bind(&ScopedCanConfigureNetwork::CanConfigureNetwork, base::Unretained(this))), needs_owner_auth_callback_(base::Bind( &ScopedCanConfigureNetwork::NeedsOwnerAuthToConfigureNetwork, base::Unretained(this))) { AppLaunchController::SetCanConfigureNetworkCallbackForTesting( &can_configure_network_callback_); AppLaunchController::SetNeedOwnerAuthToConfigureNetworkCallbackForTesting( &needs_owner_auth_callback_); } ~ScopedCanConfigureNetwork() { AppLaunchController::SetCanConfigureNetworkCallbackForTesting(NULL); AppLaunchController::SetNeedOwnerAuthToConfigureNetworkCallbackForTesting( NULL); } bool CanConfigureNetwork() { return can_configure_; } bool NeedsOwnerAuthToConfigureNetwork() { return needs_owner_auth_; } private: bool can_configure_; bool needs_owner_auth_; AppLaunchController::ReturnBoolCallback can_configure_network_callback_; AppLaunchController::ReturnBoolCallback needs_owner_auth_callback_; DISALLOW_COPY_AND_ASSIGN(ScopedCanConfigureNetwork); }; // Helper class to wait until a js condition becomes true. class JsConditionWaiter { public: JsConditionWaiter(content::WebContents* web_contents, const std::string& js) : web_contents_(web_contents), js_(js) { } void Wait() { if (CheckJs()) return; base::RepeatingTimer check_timer; check_timer.Start( FROM_HERE, base::TimeDelta::FromMilliseconds(10), this, &JsConditionWaiter::OnTimer); runner_ = new content::MessageLoopRunner; runner_->Run(); } private: bool CheckJs() { bool result; CHECK(content::ExecuteScriptAndExtractBool( web_contents_, "window.domAutomationController.send(!!(" + js_ + "));", &result)); return result; } void OnTimer() { DCHECK(runner_); if (CheckJs()) runner_->Quit(); } content::WebContents* web_contents_; const std::string js_; scoped_refptr runner_; DISALLOW_COPY_AND_ASSIGN(JsConditionWaiter); }; } // namespace class KioskTest : public OobeBaseTest { public: KioskTest() : fake_cws_(new FakeCWS) { set_exit_when_last_browser_closes(false); } virtual ~KioskTest() {} protected: virtual void SetUp() OVERRIDE { test_app_id_ = kTestKioskApp; mock_user_manager_.reset(new MockUserManager); AppLaunchController::SkipSplashWaitForTesting(); AppLaunchController::SetNetworkWaitForTesting(kTestNetworkTimeoutSeconds); OobeBaseTest::SetUp(); } virtual void CleanUpOnMainThread() OVERRIDE { AppLaunchController::SetNetworkTimeoutCallbackForTesting(NULL); AppLaunchSigninScreen::SetUserManagerForTesting(NULL); OobeBaseTest::CleanUpOnMainThread(); // Clean up while main thread still runs. // See http://crbug.com/176659. KioskAppManager::Get()->CleanUp(); } virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { OobeBaseTest::SetUpCommandLine(command_line); fake_cws_->Init(embedded_test_server()); } void LaunchApp(const std::string& app_id, bool diagnostic_mode) { bool new_kiosk_ui = KioskAppMenuHandler::EnableNewKioskUI(); GetLoginUI()->CallJavascriptFunction(new_kiosk_ui ? kLaunchAppForTestNewAPI : kLaunchAppForTestOldAPI, base::StringValue(app_id), base::FundamentalValue(diagnostic_mode)); } void ReloadKioskApps() { // Remove then add to ensure NOTIFICATION_KIOSK_APPS_LOADED fires. KioskAppManager::Get()->RemoveApp(test_app_id_); KioskAppManager::Get()->AddApp(test_app_id_); } void ReloadAutolaunchKioskApps() { KioskAppManager::Get()->AddApp(test_app_id_); KioskAppManager::Get()->SetAutoLaunchApp(test_app_id_); } void PrepareAppLaunch() { EnableConsumerKioskMode(); // Start UI content::WindowedNotificationObserver login_signal( chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()); chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); if (wizard_controller) { wizard_controller->SkipToLoginForTesting(LoginScreenContext()); login_signal.Wait(); } else { // No wizard and running with an existing profile and it should land // on account picker when new kiosk UI is enabled. Otherwise, just // wait for the login signal from Gaia. if (KioskAppMenuHandler::EnableNewKioskUI()) OobeScreenWaiter(OobeDisplay::SCREEN_ACCOUNT_PICKER).Wait(); else login_signal.Wait(); } // Wait for the Kiosk App configuration to reload. content::WindowedNotificationObserver apps_loaded_signal( chrome::NOTIFICATION_KIOSK_APPS_LOADED, content::NotificationService::AllSources()); ReloadKioskApps(); apps_loaded_signal.Wait(); } void StartAppLaunchFromLoginScreen(const base::Closure& network_setup_cb) { PrepareAppLaunch(); if (!network_setup_cb.is_null()) network_setup_cb.Run(); LaunchApp(test_app_id(), false); } const extensions::Extension* GetInstalledApp() { Profile* app_profile = ProfileManager::GetPrimaryUserProfile(); return extensions::ExtensionSystem::Get(app_profile)-> extension_service()->GetInstalledExtension(test_app_id_); } const Version& GetInstalledAppVersion() { return *GetInstalledApp()->version(); } void WaitForAppLaunchSuccess() { ExtensionTestMessageListener launch_data_check_listener("launchData.isKioskSession = true", false); // Wait for the Kiosk App to launch. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, content::NotificationService::AllSources()).Wait(); // Default profile switches to app profile after app is launched. Profile* app_profile = ProfileManager::GetPrimaryUserProfile(); ASSERT_TRUE(app_profile); // Check ChromeOS preference is initialized. EXPECT_TRUE( static_cast(app_profile)->chromeos_preferences_); // Check installer status. EXPECT_EQ(chromeos::KioskAppLaunchError::NONE, chromeos::KioskAppLaunchError::Get()); // Check if the kiosk webapp is really installed for the default profile. const extensions::Extension* app = extensions::ExtensionSystem::Get(app_profile)-> extension_service()->GetInstalledExtension(test_app_id_); EXPECT_TRUE(app); // App should appear with its window. apps::AppWindowRegistry* app_window_registry = apps::AppWindowRegistry::Get(app_profile); apps::AppWindow* window = AppWindowWaiter(app_window_registry, test_app_id_).Wait(); EXPECT_TRUE(window); // Login screen should be gone or fading out. chromeos::LoginDisplayHost* login_display_host = chromeos::LoginDisplayHostImpl::default_host(); EXPECT_TRUE( login_display_host == NULL || login_display_host->GetNativeWindow()->layer()->GetTargetOpacity() == 0.0f); // Wait until the app terminates if it is still running. if (!app_window_registry->GetAppWindowsForApp(test_app_id_).empty()) content::RunMessageLoop(); // Check that the app had been informed that it is running in a kiosk // session. EXPECT_TRUE(launch_data_check_listener.was_satisfied()); } void WaitForAppLaunchNetworkTimeout() { if (GetAppLaunchController()->network_wait_timedout()) return; scoped_refptr runner = new content::MessageLoopRunner; base::Closure callback = base::Bind( &OnNetworkWaitTimedOut, runner->QuitClosure()); AppLaunchController::SetNetworkTimeoutCallbackForTesting(&callback); runner->Run(); CHECK(GetAppLaunchController()->network_wait_timedout()); AppLaunchController::SetNetworkTimeoutCallbackForTesting(NULL); } void EnableConsumerKioskMode() { scoped_ptr locked(new bool(false)); scoped_refptr runner = new content::MessageLoopRunner; KioskAppManager::Get()->EnableConsumerKioskAutoLaunch( base::Bind(&ConsumerKioskModeAutoStartLockCheck, locked.get(), runner->QuitClosure())); runner->Run(); EXPECT_TRUE(*locked.get()); } KioskAppManager::ConsumerKioskAutoLaunchStatus GetConsumerKioskModeStatus() { KioskAppManager::ConsumerKioskAutoLaunchStatus status = static_cast(-1); scoped_refptr runner = new content::MessageLoopRunner; KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus( base::Bind(&ConsumerKioskAutoLaunchStatusCheck, &status, runner->QuitClosure())); runner->Run(); CHECK_NE(status, static_cast(-1)); return status; } // Copies the app profile from |relative_app_profile_dir| from test directory // to the app profile directory (assuming "user") under testing profile. This // is for that needs to have a kiosk app already installed from a previous // run. Note this must be called before app profile is loaded. void SetupAppProfile(const std::string& relative_app_profile_dir) { base::FilePath app_profile_dir; KioskAppManager::App app_data; CHECK(KioskAppManager::Get()->GetApp(test_app_id(), &app_data)); std::string app_user_id_hash = CryptohomeClient::GetStubSanitizedUsername(app_data.user_id); app_profile_dir = ProfileHelper::GetProfilePathByUserIdHash(app_user_id_hash); ASSERT_TRUE(base::CreateDirectory(app_profile_dir)); base::FilePath test_data_dir; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)); test_data_dir = test_data_dir.AppendASCII(relative_app_profile_dir); ASSERT_TRUE( base::CopyFile(test_data_dir.Append(chrome::kPreferencesFilename), app_profile_dir.Append(chrome::kPreferencesFilename))); ASSERT_TRUE( base::CopyDirectory(test_data_dir.AppendASCII("Extensions"), app_profile_dir, true)); } void RunAppLaunchNetworkDownTest() { mock_user_manager()->SetActiveUser(kTestOwnerEmail); AppLaunchSigninScreen::SetUserManagerForTesting(mock_user_manager()); // Mock network could be configured with owner's password. ScopedCanConfigureNetwork can_configure_network(true, true); // Start app launch and wait for network connectivity timeout. StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure()); OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH); splash_waiter.Wait(); WaitForAppLaunchNetworkTimeout(); // Configure network link should be visible. JsExpect("$('splash-config-network').hidden == false"); // Set up fake user manager with an owner for the test. static_cast(LoginDisplayHostImpl::default_host()) ->GetOobeUI()->ShowOobeUI(false); // Configure network should bring up lock screen for owner. OobeScreenWaiter lock_screen_waiter(OobeDisplay::SCREEN_ACCOUNT_PICKER); static_cast(GetAppLaunchController()) ->OnConfigureNetwork(); lock_screen_waiter.Wait(); // There should be only one owner pod on this screen. JsExpect("$('pod-row').alwaysFocusSinglePod"); // A network error screen should be shown after authenticating. OobeScreenWaiter error_screen_waiter(OobeDisplay::SCREEN_ERROR_MESSAGE); static_cast(GetAppLaunchController()) ->OnOwnerSigninSuccess(); error_screen_waiter.Wait(); ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog()); SimulateNetworkOnline(); WaitForAppLaunchSuccess(); } AppLaunchController* GetAppLaunchController() { return chromeos::LoginDisplayHostImpl::default_host() ->GetAppLaunchController(); } MockUserManager* mock_user_manager() { return mock_user_manager_.get(); } void set_test_app_id(const std::string& test_app_id) { test_app_id_ = test_app_id; } const std::string& test_app_id() const { return test_app_id_; } FakeCWS* fake_cws() { return fake_cws_.get(); } private: std::string test_app_id_; scoped_ptr fake_cws_; scoped_ptr mock_user_manager_; DISALLOW_COPY_AND_ASSIGN(KioskTest); }; IN_PROC_BROWSER_TEST_F(KioskTest, InstallAndLaunchApp) { StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, NotSignedInWithGAIAAccount) { // Tests that the kiosk session is not considered to be logged in with a GAIA // account. StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); Profile* app_profile = ProfileManager::GetPrimaryUserProfile(); ASSERT_TRUE(app_profile); EXPECT_FALSE(app_profile->GetPrefs()->HasPrefPath( prefs::kGoogleServicesUsername)); } IN_PROC_BROWSER_TEST_F(KioskTest, PRE_LaunchAppNetworkDown) { // Tests the network down case for the initial app download and launch. RunAppLaunchNetworkDownTest(); } IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDown) { // Tests the network down case for launching an existing app that is // installed in PRE_LaunchAppNetworkDown. RunAppLaunchNetworkDownTest(); } // TODO(zelidrag): Figure out why this test is flaky on bbots. IN_PROC_BROWSER_TEST_F(KioskTest, DISABLED_LaunchAppWithNetworkConfigAccelerator) { ScopedCanConfigureNetwork can_configure_network(true, false); // Start app launch and wait for network connectivity timeout. StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH); splash_waiter.Wait(); // A network error screen should be shown after authenticating. OobeScreenWaiter error_screen_waiter(OobeDisplay::SCREEN_ERROR_MESSAGE); // Simulate Ctrl+Alt+N accelerator. GetLoginUI()->CallJavascriptFunction( "cr.ui.Oobe.handleAccelerator", base::StringValue("app_launch_network_config")); error_screen_waiter.Wait(); ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog()); // Continue button should be visible since we are online. JsExpect("$('continue-network-config-btn').hidden == false"); // Click on [Continue] button. ASSERT_TRUE(content::ExecuteScript( GetLoginUI()->GetWebContents(), "(function() {" "var e = new Event('click');" "$('continue-network-config-btn').dispatchEvent(e);" "})();")); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDownConfigureNotAllowed) { // Mock network could not be configured. ScopedCanConfigureNetwork can_configure_network(false, true); // Start app launch and wait for network connectivity timeout. StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure()); OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH); splash_waiter.Wait(); WaitForAppLaunchNetworkTimeout(); // Configure network link should not be visible. JsExpect("$('splash-config-network').hidden == true"); // Network becomes online and app launch is resumed. SimulateNetworkOnline(); WaitForAppLaunchSuccess(); } // Flaky on bots. http://crbug.com/365507 IN_PROC_BROWSER_TEST_F(KioskTest, DISABLED_LaunchAppNetworkPortal) { // Mock network could be configured without the owner password. ScopedCanConfigureNetwork can_configure_network(true, false); // Start app launch with network portal state. StartAppLaunchFromLoginScreen(SimulateNetworkPortalClosure()); OobeScreenWaiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH) .WaitNoAssertCurrentScreen(); WaitForAppLaunchNetworkTimeout(); // Network error should show up automatically since this test does not // require owner auth to configure network. OobeScreenWaiter(OobeDisplay::SCREEN_ERROR_MESSAGE).Wait(); ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog()); SimulateNetworkOnline(); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppUserCancel) { StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure()); OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH); splash_waiter.Wait(); CrosSettings::Get()->SetBoolean( kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled, true); content::WindowedNotificationObserver signal( chrome::NOTIFICATION_APP_TERMINATING, content::NotificationService::AllSources()); GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("app_launch_bailout")); signal.Wait(); EXPECT_EQ(chromeos::KioskAppLaunchError::USER_CANCEL, chromeos::KioskAppLaunchError::Get()); } IN_PROC_BROWSER_TEST_F(KioskTest, LaunchInDiagnosticMode) { PrepareAppLaunch(); SimulateNetworkOnline(); LaunchApp(kTestKioskApp, true); content::WebContents* login_contents = GetLoginUI()->GetWebContents(); bool new_kiosk_ui = KioskAppMenuHandler::EnableNewKioskUI(); JsConditionWaiter(login_contents, new_kiosk_ui ? kCheckDiagnosticModeNewAPI : kCheckDiagnosticModeOldAPI).Wait(); std::string diagnosticMode(new_kiosk_ui ? kCheckDiagnosticModeNewAPI : kCheckDiagnosticModeOldAPI); ASSERT_TRUE(content::ExecuteScript( login_contents, "(function() {" "var e = new Event('click');" + diagnosticMode + "." "okButton_.dispatchEvent(e);" "})();")); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, AutolaunchWarningCancel) { EnableConsumerKioskMode(); // Start UI, find menu entry for this app and launch it. chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); ReloadAutolaunchKioskApps(); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); EXPECT_FALSE(KioskAppManager::Get()->GetAutoLaunchApp().empty()); EXPECT_FALSE(KioskAppManager::Get()->IsAutoLaunchEnabled()); // Wait for the auto launch warning come up. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction( "login.AutolaunchScreen.confirmAutoLaunchForTesting", base::FundamentalValue(false)); // Wait for the auto launch warning to go away. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_COMPLETED, content::NotificationService::AllSources()).Wait(); EXPECT_FALSE(KioskAppManager::Get()->IsAutoLaunchEnabled()); } IN_PROC_BROWSER_TEST_F(KioskTest, AutolaunchWarningConfirm) { EnableConsumerKioskMode(); // Start UI, find menu entry for this app and launch it. chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); ReloadAutolaunchKioskApps(); EXPECT_FALSE(KioskAppManager::Get()->GetAutoLaunchApp().empty()); EXPECT_FALSE(KioskAppManager::Get()->IsAutoLaunchEnabled()); // Wait for the auto launch warning come up. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction( "login.AutolaunchScreen.confirmAutoLaunchForTesting", base::FundamentalValue(true)); // Wait for the auto launch warning to go away. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_COMPLETED, content::NotificationService::AllSources()).Wait(); EXPECT_FALSE(KioskAppManager::Get()->GetAutoLaunchApp().empty()); EXPECT_TRUE(KioskAppManager::Get()->IsAutoLaunchEnabled()); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableCancel) { chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); // Check Kiosk mode status. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE, GetConsumerKioskModeStatus()); // Wait for the login UI to come up and switch to the kiosk_enable screen. wizard_controller->SkipToLoginForTesting(LoginScreenContext()); content::WindowedNotificationObserver( chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("kiosk_enable")); // Wait for the kiosk_enable screen to show and cancel the screen. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction( "login.KioskEnableScreen.enableKioskForTesting", base::FundamentalValue(false)); // Wait for the kiosk_enable screen to disappear. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_COMPLETED, content::NotificationService::AllSources()).Wait(); // Check that the status still says configurable. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE, GetConsumerKioskModeStatus()); } IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableConfirmed) { // Start UI, find menu entry for this app and launch it. chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); // Check Kiosk mode status. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE, GetConsumerKioskModeStatus()); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); // Wait for the login UI to come up and switch to the kiosk_enable screen. wizard_controller->SkipToLoginForTesting(LoginScreenContext()); content::WindowedNotificationObserver( chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("kiosk_enable")); // Wait for the kiosk_enable screen to show and cancel the screen. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction( "login.KioskEnableScreen.enableKioskForTesting", base::FundamentalValue(true)); // Wait for the signal that indicates Kiosk Mode is enabled. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLED, content::NotificationService::AllSources()).Wait(); EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED, GetConsumerKioskModeStatus()); } IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableAbortedWithAutoEnrollment) { // Fake an auto enrollment is going to be enforced. CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kEnterpriseEnrollmentInitialModulus, "1"); CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kEnterpriseEnrollmentModulusLimit, "2"); g_browser_process->local_state()->SetBoolean(prefs::kShouldAutoEnroll, true); g_browser_process->local_state()->SetInteger( prefs::kAutoEnrollmentPowerLimit, 3); // Start UI, find menu entry for this app and launch it. chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); // Check Kiosk mode status. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE, GetConsumerKioskModeStatus()); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); // Wait for the login UI to come up and switch to the kiosk_enable screen. wizard_controller->SkipToLoginForTesting(LoginScreenContext()); content::WindowedNotificationObserver( chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("kiosk_enable")); // The flow should be aborted due to auto enrollment enforcement. scoped_refptr runner = new content::MessageLoopRunner; GetSigninScreenHandler()->set_kiosk_enable_flow_aborted_callback_for_test( runner->QuitClosure()); runner->Run(); } IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableAfter2ndSigninScreen) { chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); // Check Kiosk mode status. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE, GetConsumerKioskModeStatus()); // Wait for the login UI to come up and switch to the kiosk_enable screen. wizard_controller->SkipToLoginForTesting(LoginScreenContext()); content::WindowedNotificationObserver( chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("kiosk_enable")); // Wait for the kiosk_enable screen to show and cancel the screen. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); GetLoginUI()->CallJavascriptFunction( "login.KioskEnableScreen.enableKioskForTesting", base::FundamentalValue(false)); // Wait for the kiosk_enable screen to disappear. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_COMPLETED, content::NotificationService::AllSources()).Wait(); // Show signin screen again. chromeos::LoginDisplayHostImpl::default_host()->StartSignInScreen( LoginScreenContext()); OobeScreenWaiter(OobeDisplay::SCREEN_GAIA_SIGNIN).Wait(); // Show kiosk enable screen again. GetLoginUI()->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", base::StringValue("kiosk_enable")); // And it should show up. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); } class KioskUpdateTest : public KioskTest { public: KioskUpdateTest() {} virtual ~KioskUpdateTest() {} protected: virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { // Needs background networking so that ExtensionDownloader works. needs_background_networking_ = true; KioskTest::SetUpCommandLine(command_line); } virtual void SetUpOnMainThread() OVERRIDE { KioskTest::SetUpOnMainThread(); } private: DISALLOW_COPY_AND_ASSIGN(KioskUpdateTest); }; IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppNoNetwork) { set_test_app_id(kTestOfflineEnabledKioskApp); PrepareAppLaunch(); SimulateNetworkOffline(); SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile"); LaunchApp(test_app_id(), false); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppNoUpdate) { set_test_app_id(kTestOfflineEnabledKioskApp); fake_cws()->SetNoUpdate(test_app_id()); PrepareAppLaunch(); SimulateNetworkOnline(); SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile"); LaunchApp(test_app_id(), false); WaitForAppLaunchSuccess(); EXPECT_EQ("1.0.0", GetInstalledAppVersion().GetString()); } IN_PROC_BROWSER_TEST_F(KioskUpdateTest, LaunchOfflineEnabledAppHasUpdate) { set_test_app_id(kTestOfflineEnabledKioskApp); fake_cws()->SetUpdateCrx( test_app_id(), "ajoggoflpgplnnjkjamcmbepjdjdnpdp.crx", "2.0.0"); PrepareAppLaunch(); SimulateNetworkOnline(); SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile"); LaunchApp(test_app_id(), false); WaitForAppLaunchSuccess(); EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString()); } IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PermissionChange) { set_test_app_id(kTestOfflineEnabledKioskApp); fake_cws()->SetUpdateCrx( test_app_id(), "ajoggoflpgplnnjkjamcmbepjdjdnpdp_v2_permission_change.crx", "2.0.0"); PrepareAppLaunch(); SimulateNetworkOnline(); SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile"); LaunchApp(test_app_id(), false); WaitForAppLaunchSuccess(); EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString()); } IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PRE_PreserveLocalData) { // Installs v1 app and writes some local data. set_test_app_id(kTestLocalFsKioskApp); ResultCatcher catcher; StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } IN_PROC_BROWSER_TEST_F(KioskUpdateTest, PreserveLocalData) { // Update existing v1 app installed in PRE_PreserveLocalData to v2 // that reads and verifies the local data. set_test_app_id(kTestLocalFsKioskApp); fake_cws()->SetUpdateCrx( test_app_id(), "bmbpicmpniaclbbpdkfglgipkkebnbjf_v2_read_and_verify_data.crx", "2.0.0"); ResultCatcher catcher; StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString()); ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } class KioskEnterpriseTest : public KioskTest { protected: KioskEnterpriseTest() {} virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { device_policy_test_helper_.MarkAsEnterpriseOwned(); device_policy_test_helper_.InstallOwnerKey(); KioskTest::SetUpInProcessBrowserTestFixture(); } virtual void SetUpOnMainThread() OVERRIDE { KioskTest::SetUpOnMainThread(); // Configure kTestEnterpriseKioskApp in device policy. em::DeviceLocalAccountsProto* accounts = device_policy_test_helper_.device_policy()->payload() .mutable_device_local_accounts(); em::DeviceLocalAccountInfoProto* account = accounts->add_account(); account->set_account_id(kTestEnterpriseAccountId); account->set_type( em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_KIOSK_APP); account->mutable_kiosk_app()->set_app_id(kTestEnterpriseKioskApp); accounts->set_auto_login_id(kTestEnterpriseAccountId); em::PolicyData& policy_data = device_policy_test_helper_.device_policy()->policy_data(); policy_data.set_service_account_identity(kTestEnterpriseServiceAccountId); device_policy_test_helper_.device_policy()->Build(); base::RunLoop run_loop; DBusThreadManager::Get()->GetSessionManagerClient()->StoreDevicePolicy( device_policy_test_helper_.device_policy()->GetBlob(), base::Bind(&KioskEnterpriseTest::StorePolicyCallback, run_loop.QuitClosure())); run_loop.Run(); DeviceSettingsService::Get()->Load(); // Configure OAuth authentication. GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); // This token satisfies the userinfo.email request from // DeviceOAuth2TokenService used in token validation. FakeGaia::AccessTokenInfo userinfo_token_info; userinfo_token_info.token = kTestUserinfoToken; userinfo_token_info.scopes.insert( "https://www.googleapis.com/auth/userinfo.email"); userinfo_token_info.audience = gaia_urls->oauth2_chrome_client_id(); userinfo_token_info.email = kTestEnterpriseServiceAccountId; fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_token_info); // The any-api access token for accessing the token minting endpoint. FakeGaia::AccessTokenInfo login_token_info; login_token_info.token = kTestLoginToken; login_token_info.scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); login_token_info.audience = gaia_urls->oauth2_chrome_client_id(); fake_gaia_->IssueOAuthToken(kTestRefreshToken, login_token_info); // This is the access token requested by the app via the identity API. FakeGaia::AccessTokenInfo access_token_info; access_token_info.token = kTestAccessToken; access_token_info.scopes.insert(kTestAppScope); access_token_info.audience = kTestClientId; access_token_info.email = kTestEnterpriseServiceAccountId; fake_gaia_->IssueOAuthToken(kTestLoginToken, access_token_info); DeviceOAuth2TokenService* token_service = DeviceOAuth2TokenServiceFactory::Get(); token_service->SetAndSaveRefreshToken( kTestRefreshToken, DeviceOAuth2TokenService::StatusCallback()); base::RunLoop().RunUntilIdle(); } static void StorePolicyCallback(const base::Closure& callback, bool result) { ASSERT_TRUE(result); callback.Run(); } policy::DevicePolicyCrosTestHelper device_policy_test_helper_; private: DISALLOW_COPY_AND_ASSIGN(KioskEnterpriseTest); }; IN_PROC_BROWSER_TEST_F(KioskEnterpriseTest, EnterpriseKioskApp) { chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); // Wait for the Kiosk App configuration to reload, then launch the app. KioskAppManager::App app; content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_APPS_LOADED, base::Bind(&KioskAppManager::GetApp, base::Unretained(KioskAppManager::Get()), kTestEnterpriseKioskApp, &app)).Wait(); LaunchApp(kTestEnterpriseKioskApp, false); // Wait for the Kiosk App to launch. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_APP_LAUNCHED, content::NotificationService::AllSources()).Wait(); // Check installer status. EXPECT_EQ(chromeos::KioskAppLaunchError::NONE, chromeos::KioskAppLaunchError::Get()); // Wait for the window to appear. apps::AppWindow* window = AppWindowWaiter( apps::AppWindowRegistry::Get(ProfileManager::GetPrimaryUserProfile()), kTestEnterpriseKioskApp).Wait(); ASSERT_TRUE(window); // Check whether the app can retrieve an OAuth2 access token. std::string result; EXPECT_TRUE(content::ExecuteScriptAndExtractString( window->web_contents(), "chrome.identity.getAuthToken({ 'interactive': false }, function(token) {" " window.domAutomationController.setAutomationId(0);" " window.domAutomationController.send(token);" "});", &result)); EXPECT_EQ(kTestAccessToken, result); // Verify that the session is not considered to be logged in with a GAIA // account. Profile* app_profile = ProfileManager::GetPrimaryUserProfile(); ASSERT_TRUE(app_profile); EXPECT_FALSE(app_profile->GetPrefs()->HasPrefPath( prefs::kGoogleServicesUsername)); // Terminate the app. window->GetBaseWindow()->Close(); content::RunAllPendingInMessageLoop(); } // Specialized test fixture for testing kiosk mode on the // hidden WebUI initialization flow for slow hardware. class KioskHiddenWebUITest : public KioskTest, public ash::DesktopBackgroundControllerObserver { public: KioskHiddenWebUITest() : wallpaper_loaded_(false) {} // KioskTest overrides: virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { KioskTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kDisableBootAnimation); } virtual void SetUpOnMainThread() OVERRIDE { KioskTest::SetUpOnMainThread(); ash::Shell::GetInstance()->desktop_background_controller() ->AddObserver(this); StartupUtils::MarkDeviceRegistered(base::Closure()); } virtual void TearDownOnMainThread() OVERRIDE { ash::Shell::GetInstance()->desktop_background_controller() ->RemoveObserver(this); KioskTest::TearDownOnMainThread(); } void WaitForWallpaper() { if (!wallpaper_loaded_) { runner_ = new content::MessageLoopRunner; runner_->Run(); } } bool wallpaper_loaded() const { return wallpaper_loaded_; } // ash::DesktopBackgroundControllerObserver overrides: virtual void OnWallpaperDataChanged() OVERRIDE { wallpaper_loaded_ = true; if (runner_.get()) runner_->Quit(); } bool wallpaper_loaded_; scoped_refptr runner_; DISALLOW_COPY_AND_ASSIGN(KioskHiddenWebUITest); }; IN_PROC_BROWSER_TEST_F(KioskHiddenWebUITest, AutolaunchWarning) { // Add a device owner. FakeUserManager* user_manager = new FakeUserManager(); user_manager->AddUser(kTestOwnerEmail); ScopedUserManagerEnabler enabler(user_manager); // Set kiosk app to autolaunch. EnableConsumerKioskMode(); chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); ReloadAutolaunchKioskApps(); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); EXPECT_FALSE(KioskAppManager::Get()->GetAutoLaunchApp().empty()); EXPECT_FALSE(KioskAppManager::Get()->IsAutoLaunchEnabled()); // Wait for the auto launch warning come up. content::WindowedNotificationObserver( chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_VISIBLE, content::NotificationService::AllSources()).Wait(); // Wait for the wallpaper to load. WaitForWallpaper(); EXPECT_TRUE(wallpaper_loaded()); } } // namespace chromeos