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