// 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/shell_window.h" #include "apps/shell_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/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/command_line.h" #include "base/location.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/prefs/scoped_user_pref_update.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_browser_main.h" #include "chrome/browser/chrome_browser_main_extra_parts.h" #include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/chrome_notification_types.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/app_launch_signin_screen.h" #include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/fake_user_manager.h" #include "chrome/browser/chromeos/login/login_display_host_impl.h" #include "chrome/browser/chromeos/login/mock_user_manager.h" #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" #include "chrome/browser/chromeos/login/webui_login_display.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.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/settings/cros_settings.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_system.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "chromeos/chromeos_switches.h" #include "chromeos/settings/cros_settings_names.h" #include "components/policy/core/common/cloud/policy_builder.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 "content/public/test/test_utils.h" #include "extensions/common/extension.h" #include "google_apis/gaia/fake_gaia.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_switches.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/network_change_notifier.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/window.h" #include "ui/compositor/layer.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"; // 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"; // Note the path name must be the same as in shill stub. const char kStubEthernetServicePath[] = "eth1"; // Helper function for GetConsumerKioskModeStatusCallback. void ConsumerKioskModeStatusCheck( KioskAppManager::ConsumerKioskModeStatus* out_status, const base::Closure& runner_quit_task, KioskAppManager::ConsumerKioskModeStatus in_status) { LOG(INFO) << "KioskAppManager::ConsumerKioskModeStatus = " << in_status; *out_status = in_status; runner_quit_task.Run(); } // Helper KioskAppManager::EnableKioskModeCallback implementation. void ConsumerKioskModeLockCheck( 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 function for DeviceOAuth2TokenServiceFactory::Get(). void CopyTokenService(DeviceOAuth2TokenService** out_token_service, DeviceOAuth2TokenService* in_token_service) { *out_token_service = in_token_service; } // 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); }; } // namespace // Helper class that monitors app windows to wait for a window to appear. class ShellWindowObserver : public apps::ShellWindowRegistry::Observer { public: ShellWindowObserver(apps::ShellWindowRegistry* registry, const std::string& app_id) : registry_(registry), app_id_(app_id), window_(NULL), running_(false) { registry_->AddObserver(this); } virtual ~ShellWindowObserver() { registry_->RemoveObserver(this); } apps::ShellWindow* Wait() { running_ = true; message_loop_runner_ = new content::MessageLoopRunner; message_loop_runner_->Run(); EXPECT_TRUE(window_); return window_; } // ShellWindowRegistry::Observer virtual void OnShellWindowAdded(apps::ShellWindow* shell_window) OVERRIDE { if (!running_) return; if (shell_window->extension_id() == app_id_) { window_ = shell_window; message_loop_runner_->Quit(); running_ = false; } } virtual void OnShellWindowIconChanged( apps::ShellWindow* shell_window) OVERRIDE {} virtual void OnShellWindowRemoved(apps::ShellWindow* shell_window) OVERRIDE {} private: apps::ShellWindowRegistry* registry_; std::string app_id_; scoped_refptr<content::MessageLoopRunner> message_loop_runner_; apps::ShellWindow* window_; bool running_; DISALLOW_COPY_AND_ASSIGN(ShellWindowObserver); }; class KioskTest : public InProcessBrowserTest { public: KioskTest() { set_exit_when_last_browser_closes(false); } virtual ~KioskTest() {} protected: virtual void SetUp() OVERRIDE { base::FilePath test_data_dir; PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); embedded_test_server()->ServeFilesFromDirectory(test_data_dir); embedded_test_server()->RegisterRequestHandler( base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); // Stop IO thread here because no threads are allowed while // spawning sandbox host process. See crbug.com/322732. embedded_test_server()->StopThread(); mock_user_manager_.reset(new MockUserManager); AppLaunchController::SkipSplashWaitForTesting(); AppLaunchController::SetNetworkWaitForTesting(kTestNetworkTimeoutSeconds); InProcessBrowserTest::SetUp(); } virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { host_resolver()->AddRule("*", "127.0.0.1"); network_portal_detector_ = new NetworkPortalDetectorTestImpl(); NetworkPortalDetector::InitializeForTesting(network_portal_detector_); network_portal_detector_->SetDefaultNetworkPathForTesting( kStubEthernetServicePath); } virtual void SetUpOnMainThread() OVERRIDE { // Restart the thread as the sandbox host process has already been spawned. embedded_test_server()->RestartThreadAndListen(); } virtual void CleanUpOnMainThread() OVERRIDE { AppLaunchController::SetNetworkTimeoutCallbackForTesting(NULL); AppLaunchSigninScreen::SetUserManagerForTesting(NULL); // If the login display is still showing, exit gracefully. if (LoginDisplayHostImpl::default_host()) { base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&chrome::AttemptExit)); content::RunMessageLoop(); } // Clean up while main thread still runs. // See http://crbug.com/176659. KioskAppManager::Get()->CleanUp(); } virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { command_line->AppendSwitch(chromeos::switches::kLoginManager); command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); command_line->AppendSwitch(::switches::kDisableBackgroundNetworking); command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); // Create gaia and webstore URL from test server url but using different // host names. This is to avoid gaia response being tagged as from // webstore in chrome_resource_dispatcher_host_delegate.cc. const GURL& server_url = embedded_test_server()->base_url(); std::string gaia_host("gaia"); GURL::Replacements replace_gaia_host; replace_gaia_host.SetHostStr(gaia_host); GURL gaia_url = server_url.ReplaceComponents(replace_gaia_host); command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec()); command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec()); command_line->AppendSwitchASCII(::switches::kGoogleApisUrl, gaia_url.spec()); std::string webstore_host("webstore"); GURL::Replacements replace_webstore_host; replace_webstore_host.SetHostStr(webstore_host); GURL webstore_url = server_url.ReplaceComponents(replace_webstore_host); command_line->AppendSwitchASCII( ::switches::kAppsGalleryURL, webstore_url.Resolve("/chromeos/app_mode/webstore").spec()); command_line->AppendSwitchASCII( ::switches::kAppsGalleryDownloadURL, webstore_url.Resolve( "/chromeos/app_mode/webstore/downloads/%s.crx").spec()); } void ReloadKioskApps() { KioskAppManager::Get()->AddApp(kTestKioskApp); } void ReloadAutolaunchKioskApps() { KioskAppManager::Get()->AddApp(kTestKioskApp); KioskAppManager::Get()->SetAutoLaunchApp(kTestKioskApp); } void StartAppLaunchFromLoginScreen(const base::Closure& network_setup_cb) { EnableConsumerKioskMode(); // Start UI, find menu entry for this app and launch it. 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(); CHECK(wizard_controller); wizard_controller->SkipToLoginForTesting(LoginScreenContext()); login_signal.Wait(); // Wait for the Kiosk App configuration to reload, then launch the app. content::WindowedNotificationObserver apps_loaded_signal( chrome::NOTIFICATION_KIOSK_APPS_LOADED, content::NotificationService::AllSources()); ReloadKioskApps(); apps_loaded_signal.Wait(); if (!network_setup_cb.is_null()) network_setup_cb.Run(); GetLoginUI()->CallJavascriptFunction( "login.AppsMenuButton.runAppForTesting", base::StringValue(kTestKioskApp)); } void WaitForAppLaunchSuccess() { SimulateNetworkOnline(); 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 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(kTestKioskApp); EXPECT_TRUE(app); // App should appear with its window. apps::ShellWindowRegistry* shell_window_registry = apps::ShellWindowRegistry::Get(app_profile); apps::ShellWindow* window = ShellWindowObserver(shell_window_registry, kTestKioskApp).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 (!shell_window_registry->GetShellWindowsForApp(kTestKioskApp).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 SimulateNetworkOffline() { NetworkPortalDetector::CaptivePortalState offline_state; offline_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE; network_portal_detector_->SetDetectionResultsForTesting( kStubEthernetServicePath, offline_state); network_portal_detector_->NotifyObserversForTesting(); } base::Closure SimulateNetworkOfflineClosure() { return base::Bind(&KioskTest::SimulateNetworkOffline, base::Unretained(this)); } void SimulateNetworkOnline() { NetworkPortalDetector::CaptivePortalState online_state; online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; online_state.response_code = 204; network_portal_detector_->SetDetectionResultsForTesting( kStubEthernetServicePath, online_state); network_portal_detector_->NotifyObserversForTesting(); } base::Closure SimulateNetworkOnlineClosure() { return base::Bind(&KioskTest::SimulateNetworkOnline, base::Unretained(this)); } void SimulateNetworkPortal() { NetworkPortalDetector::CaptivePortalState portal_state; portal_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL; network_portal_detector_->SetDetectionResultsForTesting( kStubEthernetServicePath, portal_state); network_portal_detector_->NotifyObserversForTesting(); } base::Closure SimulateNetworkPortalClosure() { return base::Bind(&KioskTest::SimulateNetworkPortal, base::Unretained(this)); } void WaitForAppLaunchNetworkTimeout() { if (GetAppLaunchController()->network_wait_timedout()) return; scoped_refptr<content::MessageLoopRunner> 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<bool> locked(new bool(false)); scoped_refptr<content::MessageLoopRunner> runner = new content::MessageLoopRunner; KioskAppManager::Get()->EnableConsumerModeKiosk( base::Bind(&ConsumerKioskModeLockCheck, locked.get(), runner->QuitClosure())); runner->Run(); EXPECT_TRUE(*locked.get()); } KioskAppManager::ConsumerKioskModeStatus GetConsumerKioskModeStatus() { KioskAppManager::ConsumerKioskModeStatus status = static_cast<KioskAppManager::ConsumerKioskModeStatus>(-1); scoped_refptr<content::MessageLoopRunner> runner = new content::MessageLoopRunner; KioskAppManager::Get()->GetConsumerKioskModeStatus( base::Bind(&ConsumerKioskModeStatusCheck, &status, runner->QuitClosure())); runner->Run(); CHECK_NE(status, static_cast<KioskAppManager::ConsumerKioskModeStatus>(-1)); return status; } void JsExpect(const std::string& expression) { bool result; ASSERT_TRUE(content::ExecuteScriptAndExtractBool( GetLoginUI()->GetWebContents(), "window.domAutomationController.send(!!(" + expression + "));", &result)); ASSERT_TRUE(result) << expression; } content::WebUI* GetLoginUI() { return static_cast<chromeos::LoginDisplayHostImpl*>( chromeos::LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui(); } SigninScreenHandler* GetSigninScreenHandler() { return static_cast<chromeos::LoginDisplayHostImpl*>( chromeos::LoginDisplayHostImpl::default_host()) ->GetOobeUI() ->signin_screen_handler_for_test(); } AppLaunchController* GetAppLaunchController() { return chromeos::LoginDisplayHostImpl::default_host() ->GetAppLaunchController(); } FakeGaia fake_gaia_; scoped_ptr<MockUserManager> mock_user_manager_; NetworkPortalDetectorTestImpl* network_portal_detector_; }; IN_PROC_BROWSER_TEST_F(KioskTest, InstallAndLaunchApp) { StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure()); WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDown) { // 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. mock_user_manager_->SetActiveUser(kTestOwnerEmail); AppLaunchSigninScreen::SetUserManagerForTesting(mock_user_manager_.get()); static_cast<LoginDisplayHostImpl*>(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<AppLaunchSplashScreenActor::Delegate*>(GetAppLaunchController()) ->OnConfigureNetwork(); lock_screen_waiter.Wait(); // A network error screen should be shown after authenticating. OobeScreenWaiter error_screen_waiter(OobeDisplay::SCREEN_ERROR_MESSAGE); static_cast<AppLaunchSigninScreen::Delegate*>(GetAppLaunchController()) ->OnOwnerSigninSuccess(); error_screen_waiter.Wait(); ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog()); 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. WaitForAppLaunchSuccess(); } IN_PROC_BROWSER_TEST_F(KioskTest, 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()); 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, 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_MODE_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_MODE_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_MODE_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_MODE_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_MODE_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<content::MessageLoopRunner> runner = new content::MessageLoopRunner; GetSigninScreenHandler()->set_kiosk_enable_flow_aborted_callback_for_test( runner->QuitClosure()); runner->Run(); } IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableAfter2ndSigninScreen) { // Fake an auto enrollment is not going to be enforced. CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kEnterpriseEnrollmentInitialModulus, "1"); CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kEnterpriseEnrollmentModulusLimit, "2"); g_browser_process->local_state()->SetBoolean(prefs::kShouldAutoEnroll, false); g_browser_process->local_state()->SetInteger( prefs::kAutoEnrollmentPowerLimit, -1); chromeos::WizardController::SkipPostLoginScreensForTesting(); chromeos::WizardController* wizard_controller = chromeos::WizardController::default_controller(); CHECK(wizard_controller); // Check Kiosk mode status. EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_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 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(); DBusThreadManager::Get()->GetSessionManagerClient()->StoreDevicePolicy( device_policy_test_helper_.device_policy()->GetBlob(), base::Bind(&KioskEnterpriseTest::StorePolicyCallback)); 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 = NULL; DeviceOAuth2TokenServiceFactory::Get( base::Bind(&CopyTokenService, &token_service)); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(token_service); token_service->SetAndSaveRefreshToken(kTestRefreshToken); } static void StorePolicyCallback(bool result) { ASSERT_TRUE(result); } 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(); GetLoginUI()->CallJavascriptFunction( "login.AppsMenuButton.runAppForTesting", base::StringValue(kTestEnterpriseKioskApp)); // 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::ShellWindow* window = ShellWindowObserver( apps::ShellWindowRegistry::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.send(token);" "});", &result)); EXPECT_EQ(kTestAccessToken, result); // 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->AppendSwitchASCII(switches::kDeviceRegistered, "1"); command_line->AppendSwitch(switches::kDisableBootAnimation); command_line->AppendSwitch(switches::kDisableOobeAnimation); } virtual void SetUpOnMainThread() OVERRIDE { KioskTest::SetUpOnMainThread(); ash::Shell::GetInstance()->desktop_background_controller() ->AddObserver(this); } 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<content::MessageLoopRunner> 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