1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <string>
7 #include <vector>
8
9 #include "apps/test/app_window_waiter.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/run_loop.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/chromeos/app_mode/fake_cws.h"
16 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
17 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
18 #include "chrome/browser/chromeos/login/app_mode/kiosk_launch_controller.h"
19 #include "chrome/browser/chromeos/login/test/device_state_mixin.h"
20 #include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
21 #include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
22 #include "chrome/browser/chromeos/policy/device_local_account.h"
23 #include "chrome/browser/extensions/browsertest_util.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
30 #include "chromeos/dbus/shill/shill_manager_client.h"
31 #include "chromeos/tpm/stub_install_attributes.h"
32 #include "components/crx_file/crx_verifier.h"
33 #include "content/public/browser/notification_observer.h"
34 #include "content/public/browser/notification_registrar.h"
35 #include "content/public/browser/notification_service.h"
36 #include "content/public/test/browser_test.h"
37 #include "content/public/test/test_utils.h"
38 #include "extensions/browser/app_window/app_window.h"
39 #include "extensions/browser/app_window/app_window_registry.h"
40 #include "extensions/browser/app_window/native_app_window.h"
41 #include "extensions/browser/sandboxed_unpacker.h"
42 #include "extensions/test/extension_test_message_listener.h"
43 #include "extensions/test/result_catcher.h"
44 #include "net/dns/mock_host_resolver.h"
45
46 namespace em = enterprise_management;
47
48 namespace chromeos {
49
50 namespace {
51
52 // This is a simple test app that creates an app window and immediately closes
53 // it again. Webstore data json is in
54 // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/
55 // detail/ggaeimfdpnmlhdhpcikgoblffmkckdmn
56 constexpr char kTestKioskApp[] = "ggaeimfdpnmlhdhpcikgoblffmkckdmn";
57
58 // This is a simple test that only sends an extension message when app launch is
59 // requested. Webstore data json is in
60 // chrome/test/data/chromeos/app_mode/webstore/inlineinstall/
61 // detail/gbcgichpbeeimejckkpgnaighpndpped
62 constexpr char kTestNonKioskEnabledApp[] = "gbcgichpbeeimejckkpgnaighpndpped";
63
64 // Primary kiosk app that runs tests for chrome.management API.
65 // The tests are run on the kiosk app launch event.
66 // It has a secondary test kiosk app, which is loaded alongside the app. The
67 // secondary app will send a message to run chrome.management API tests in
68 // in its context as well.
69 // The app's CRX is located under:
70 // chrome/test/data/chromeos/app_mode/webstore/downloads/
71 // adinpkdaebaiabdlinlbjmenialdhibc.crx
72 // Source from which the CRX is generated is under path:
73 // chrome/test/data/chromeos/app_mode/management_api/primary_app/
74 constexpr char kTestManagementApiKioskApp[] =
75 "adinpkdaebaiabdlinlbjmenialdhibc";
76
77 // Secondary kiosk app that runs tests for chrome.management API.
78 // The app is loaded alongside `kTestManagementApiKioskApp`. The tests are run
79 // in the response to a message sent from `kTestManagementApiKioskApp`.
80 // The app's CRX is located under:
81 // chrome/test/data/chromeos/app_mode/webstore/downloads/
82 // kajpgkhinciaiihghpdamekpjpldgpfi.crx
83 // Source from which the CRX is generated is under path:
84 // chrome/test/data/chromeos/app_mode/management_api/secondary_app/
85 constexpr char kTestManagementApiSecondaryApp[] =
86 "kajpgkhinciaiihghpdamekpjpldgpfi";
87
88 constexpr char kTestAccountId[] = "enterprise-kiosk-app@localhost";
89
90 // Used to listen for app termination notification.
91 class TerminationObserver : public content::NotificationObserver {
92 public:
TerminationObserver()93 TerminationObserver() {
94 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
95 content::NotificationService::AllSources());
96 }
97 ~TerminationObserver() override = default;
98
99 // Whether app has been terminated - i.e. whether app termination notification
100 // has been observed.
terminated() const101 bool terminated() const { return notification_seen_; }
102
103 private:
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)104 void Observe(int type,
105 const content::NotificationSource& source,
106 const content::NotificationDetails& details) override {
107 ASSERT_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
108 notification_seen_ = true;
109 }
110
111 bool notification_seen_ = false;
112 content::NotificationRegistrar registrar_;
113
114 DISALLOW_COPY_AND_ASSIGN(TerminationObserver);
115 };
116
117 } // namespace
118
119 class AutoLaunchedKioskTest : public MixinBasedInProcessBrowserTest {
120 public:
AutoLaunchedKioskTest()121 AutoLaunchedKioskTest()
122 : verifier_format_override_(crx_file::VerifierFormat::CRX3) {
123 device_state_.set_domain("domain.com");
124 }
125
126 ~AutoLaunchedKioskTest() override = default;
127
GetTestAppId() const128 virtual std::string GetTestAppId() const { return kTestKioskApp; }
GetTestSecondaryAppIds() const129 virtual std::vector<std::string> GetTestSecondaryAppIds() const {
130 return std::vector<std::string>();
131 }
132
SetUp()133 void SetUp() override {
134 skip_splash_wait_override_ =
135 KioskLaunchController::SkipSplashScreenWaitForTesting();
136 login_manager_.set_session_restore_enabled();
137 login_manager_.SetDefaultLoginSwitches(
138 {std::make_pair("test_switch_1", ""),
139 std::make_pair("test_switch_2", "test_switch_2_value")});
140 MixinBasedInProcessBrowserTest::SetUp();
141 }
142
SetUpCommandLine(base::CommandLine * command_line)143 void SetUpCommandLine(base::CommandLine* command_line) override {
144 fake_cws_.Init(embedded_test_server());
145 fake_cws_.SetUpdateCrx(GetTestAppId(), GetTestAppId() + ".crx", "1.0.0");
146
147 std::vector<std::string> secondary_apps = GetTestSecondaryAppIds();
148 for (const auto& secondary_app : secondary_apps)
149 fake_cws_.SetUpdateCrx(secondary_app, secondary_app + ".crx", "1.0.0");
150
151 MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
152 }
153
SetUpInProcessBrowserTestFixture()154 void SetUpInProcessBrowserTestFixture() override {
155 host_resolver()->AddRule("*", "127.0.0.1");
156
157 SessionManagerClient::InitializeFakeInMemory();
158
159 FakeSessionManagerClient::Get()->set_supports_browser_restart(true);
160
161 std::unique_ptr<ScopedDevicePolicyUpdate> device_policy_update =
162 device_state_.RequestDevicePolicyUpdate();
163 em::DeviceLocalAccountsProto* const device_local_accounts =
164 device_policy_update->policy_payload()->mutable_device_local_accounts();
165 device_local_accounts->set_auto_login_id(kTestAccountId);
166
167 em::DeviceLocalAccountInfoProto* const account =
168 device_local_accounts->add_account();
169 account->set_account_id(kTestAccountId);
170 account->set_type(em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_KIOSK_APP);
171 account->mutable_kiosk_app()->set_app_id(GetTestAppId());
172
173 device_policy_update.reset();
174
175 std::unique_ptr<ScopedUserPolicyUpdate> device_local_account_policy_update =
176 device_state_.RequestDeviceLocalAccountPolicyUpdate(kTestAccountId);
177 device_local_account_policy_update.reset();
178
179 MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
180 }
181
PreRunTestOnMainThread()182 void PreRunTestOnMainThread() override {
183 // Initialize extension test message listener early on, as the test kiosk
184 // app gets launched early in Chrome session setup for CrashRestore test.
185 // Listeners created in IN_PROC_BROWSER_TEST might miss the messages sent
186 // from the kiosk app.
187 app_window_loaded_listener_ =
188 std::make_unique<ExtensionTestMessageListener>("appWindowLoaded",
189 false);
190 termination_observer_ = std::make_unique<TerminationObserver>();
191 InProcessBrowserTest::PreRunTestOnMainThread();
192 }
193
SetUpOnMainThread()194 void SetUpOnMainThread() override {
195 extensions::browsertest_util::CreateAndInitializeLocalCache();
196 MixinBasedInProcessBrowserTest::SetUpOnMainThread();
197 }
198
TearDownOnMainThread()199 void TearDownOnMainThread() override {
200 app_window_loaded_listener_.reset();
201 termination_observer_.reset();
202
203 MixinBasedInProcessBrowserTest::TearDownOnMainThread();
204 }
205
GetTestAppUserId() const206 const std::string GetTestAppUserId() const {
207 return policy::GenerateDeviceLocalAccountUserId(
208 kTestAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
209 }
210
CloseAppWindow(const std::string & app_id)211 bool CloseAppWindow(const std::string& app_id) {
212 Profile* const app_profile = ProfileManager::GetPrimaryUserProfile();
213 if (!app_profile) {
214 ADD_FAILURE() << "No primary (app) profile.";
215 return false;
216 }
217
218 extensions::AppWindowRegistry* const app_window_registry =
219 extensions::AppWindowRegistry::Get(app_profile);
220 extensions::AppWindow* const window =
221 apps::AppWindowWaiter(app_window_registry, app_id).Wait();
222 if (!window) {
223 ADD_FAILURE() << "No app window found for " << app_id << ".";
224 return false;
225 }
226
227 window->GetBaseWindow()->Close();
228
229 // Wait until the app terminates if it is still running.
230 if (!app_window_registry->GetAppWindowsForApp(app_id).empty())
231 RunUntilBrowserProcessQuits();
232 return true;
233 }
234
IsKioskAppAutoLaunched(const std::string & app_id)235 bool IsKioskAppAutoLaunched(const std::string& app_id) {
236 KioskAppManager::App app;
237 if (!KioskAppManager::Get()->GetApp(app_id, &app)) {
238 ADD_FAILURE() << "App " << app_id << " not found.";
239 return false;
240 }
241 return app.was_auto_launched_with_zero_delay;
242 }
243
ExpectCommandLineHasDefaultPolicySwitches(const base::CommandLine & cmd_line)244 void ExpectCommandLineHasDefaultPolicySwitches(
245 const base::CommandLine& cmd_line) {
246 EXPECT_TRUE(cmd_line.HasSwitch("test_switch_1"));
247 EXPECT_EQ("", cmd_line.GetSwitchValueASCII("test_switch_1"));
248 EXPECT_TRUE(cmd_line.HasSwitch("test_switch_2"));
249 EXPECT_EQ("test_switch_2_value",
250 cmd_line.GetSwitchValueASCII("test_switch_2"));
251 }
252
253 protected:
254 std::unique_ptr<ExtensionTestMessageListener> app_window_loaded_listener_;
255 std::unique_ptr<TerminationObserver> termination_observer_;
256
257 private:
258 FakeCWS fake_cws_;
259 extensions::SandboxedUnpacker::ScopedVerifierFormatOverrideForTest
260 verifier_format_override_;
261 std::unique_ptr<base::AutoReset<bool>> skip_splash_wait_override_;
262
263 EmbeddedTestServerSetupMixin embedded_test_server_setup_{
264 &mixin_host_, embedded_test_server()};
265 LoginManagerMixin login_manager_{&mixin_host_, {}};
266
267 DeviceStateMixin device_state_{
268 &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
269
270 DISALLOW_COPY_AND_ASSIGN(AutoLaunchedKioskTest);
271 };
272
IN_PROC_BROWSER_TEST_F(AutoLaunchedKioskTest,PRE_CrashRestore)273 IN_PROC_BROWSER_TEST_F(AutoLaunchedKioskTest, PRE_CrashRestore) {
274 // Verify that Chrome hasn't already exited, e.g. in order to apply user
275 // session flags.
276 ASSERT_FALSE(termination_observer_->terminated());
277
278 // Set up default network connections, so tests think the device is online.
279 DBusThreadManager::Get()
280 ->GetShillManagerClient()
281 ->GetTestInterface()
282 ->SetupDefaultEnvironment();
283
284 // Check that policy flags have not been lost.
285 ExpectCommandLineHasDefaultPolicySwitches(
286 *base::CommandLine::ForCurrentProcess());
287
288 EXPECT_TRUE(app_window_loaded_listener_->WaitUntilSatisfied());
289
290 EXPECT_TRUE(IsKioskAppAutoLaunched(kTestKioskApp));
291
292 ASSERT_TRUE(CloseAppWindow(kTestKioskApp));
293 }
294
IN_PROC_BROWSER_TEST_F(AutoLaunchedKioskTest,CrashRestore)295 IN_PROC_BROWSER_TEST_F(AutoLaunchedKioskTest, CrashRestore) {
296 // Verify that Chrome hasn't already exited, e.g. in order to apply user
297 // session flags.
298 ASSERT_FALSE(termination_observer_->terminated());
299
300 ExpectCommandLineHasDefaultPolicySwitches(
301 *base::CommandLine::ForCurrentProcess());
302
303 EXPECT_TRUE(app_window_loaded_listener_->WaitUntilSatisfied());
304
305 EXPECT_TRUE(IsKioskAppAutoLaunched(kTestKioskApp));
306
307 ASSERT_TRUE(CloseAppWindow(kTestKioskApp));
308 }
309
310 // Used to test app auto-launch flow when the launched app is not kiosk enabled.
311 class AutoLaunchedNonKioskEnabledAppTest : public AutoLaunchedKioskTest {
312 public:
AutoLaunchedNonKioskEnabledAppTest()313 AutoLaunchedNonKioskEnabledAppTest() {}
314 ~AutoLaunchedNonKioskEnabledAppTest() override = default;
315
GetTestAppId() const316 std::string GetTestAppId() const override { return kTestNonKioskEnabledApp; }
317
318 private:
319 DISALLOW_COPY_AND_ASSIGN(AutoLaunchedNonKioskEnabledAppTest);
320 };
321
IN_PROC_BROWSER_TEST_F(AutoLaunchedNonKioskEnabledAppTest,NotLaunched)322 IN_PROC_BROWSER_TEST_F(AutoLaunchedNonKioskEnabledAppTest, NotLaunched) {
323 // Verify that Chrome hasn't already exited, e.g. in order to apply user
324 // session flags.
325 ASSERT_FALSE(termination_observer_->terminated());
326
327 EXPECT_TRUE(IsKioskAppAutoLaunched(kTestNonKioskEnabledApp));
328
329 ExtensionTestMessageListener listener("launchRequested", false);
330
331 content::WindowedNotificationObserver termination_waiter(
332 chrome::NOTIFICATION_APP_TERMINATING,
333 content::NotificationService::AllSources());
334
335 // Set up default network connections, so tests think the device is online.
336 DBusThreadManager::Get()
337 ->GetShillManagerClient()
338 ->GetTestInterface()
339 ->SetupDefaultEnvironment();
340
341 // App launch should be canceled, and user session stopped.
342 termination_waiter.Wait();
343
344 EXPECT_FALSE(listener.was_satisfied());
345 EXPECT_EQ(KioskAppLaunchError::NOT_KIOSK_ENABLED, KioskAppLaunchError::Get());
346 }
347
348 // Used to test management API availability in kiosk sessions.
349 class ManagementApiKioskTest : public AutoLaunchedKioskTest {
350 public:
ManagementApiKioskTest()351 ManagementApiKioskTest() {}
352 ~ManagementApiKioskTest() override = default;
353
354 // AutoLaunchedKioskTest:
GetTestAppId() const355 std::string GetTestAppId() const override {
356 return kTestManagementApiKioskApp;
357 }
GetTestSecondaryAppIds() const358 std::vector<std::string> GetTestSecondaryAppIds() const override {
359 return {kTestManagementApiSecondaryApp};
360 }
361
362 private:
363 DISALLOW_COPY_AND_ASSIGN(ManagementApiKioskTest);
364 };
365
IN_PROC_BROWSER_TEST_F(ManagementApiKioskTest,ManagementApi)366 IN_PROC_BROWSER_TEST_F(ManagementApiKioskTest, ManagementApi) {
367 // Set up default network connections, so tests think the device is online.
368 DBusThreadManager::Get()
369 ->GetShillManagerClient()
370 ->GetTestInterface()
371 ->SetupDefaultEnvironment();
372
373 // The tests expects to recieve two test result messages:
374 // * result for tests run by the secondary kiosk app.
375 // * result for tests run by the primary kiosk app.
376 extensions::ResultCatcher catcher;
377 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
378 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
379 }
380
381 } // namespace chromeos
382