// 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