OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string> | |
6 | |
7 #include "base/bind.h" | |
5 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/files/file_path.h" | |
10 #include "base/location.h" | |
11 #include "base/logging.h" | |
6 #include "base/macros.h" | 12 #include "base/macros.h" |
13 #include "base/strings/string_util.h" | |
7 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
15 #include "chrome/browser/chromeos/policy/device_policy_builder.h" | |
16 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" | |
17 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" | |
8 #include "chrome/browser/chromeos/profiles/profile_helper.h" | 18 #include "chrome/browser/chromeos/profiles/profile_helper.h" |
19 #include "chrome/browser/extensions/crx_installer.h" | |
9 #include "chrome/browser/extensions/extension_service.h" | 20 #include "chrome/browser/extensions/extension_service.h" |
21 #include "chrome/browser/net/url_request_mock_util.h" | |
10 #include "chrome/browser/profiles/profile_manager.h" | 22 #include "chrome/browser/profiles/profile_manager.h" |
11 #include "chrome/common/chrome_switches.h" | 23 #include "chrome/common/chrome_switches.h" |
12 #include "chrome/test/base/in_process_browser_test.h" | 24 #include "chrome/test/base/in_process_browser_test.h" |
25 #include "chromeos/chromeos_switches.h" | |
26 #include "chromeos/dbus/fake_session_manager_client.h" | |
27 #include "components/version_info/channel.h" | |
28 #include "components/version_info/version_info.h" | |
29 #include "content/public/browser/browser_thread.h" | |
30 #include "content/public/browser/notification_source.h" | |
13 #include "content/public/test/browser_test.h" | 31 #include "content/public/test/browser_test.h" |
32 #include "content/public/test/test_utils.h" | |
33 #include "extensions/browser/extension_registry.h" | |
14 #include "extensions/browser/extension_system.h" | 34 #include "extensions/browser/extension_system.h" |
35 #include "extensions/browser/notification_types.h" | |
36 #include "extensions/browser/test_extension_registry_observer.h" | |
37 #include "extensions/common/extension.h" | |
38 #include "extensions/common/extension_set.h" | |
39 #include "extensions/common/features/feature_channel.h" | |
40 #include "net/test/url_request/url_request_mock_http_job.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | 41 #include "testing/gtest/include/gtest/gtest.h" |
42 #include "url/gurl.h" | |
16 | 43 |
17 namespace policy { | 44 namespace policy { |
18 | 45 |
19 namespace { | 46 namespace { |
20 | 47 |
48 // Parameters for the several extensions and apps that are used by the tests in | |
49 // this file (note that the paths are given relative to the src/chrome/test/data | |
50 // directory): | |
51 // * The test app which is whitelisted for running in the sign-in profile: | |
52 const char kTestAppId[] = "bjaiihebfngildkcjkjckolinodhliff"; | |
53 const char kTestAppUpdateManifestPath[] = | |
54 "extensions/signin_screen_test_app/update_manifest.xml"; | |
55 // * A trivial test app which is NOT whitelisted for running in the sign-in | |
56 // profile: | |
57 const char kTrivialAppId[] = "mockapnacjbcdncmpkjngjalkhphojek"; | |
58 const char kTrivialAppUpdateManifestPath[] = | |
59 "extensions/trivial_platform_app/update_manifest.xml"; | |
60 // * A trivial test extension (note that extensions cannot be whitelisted for | |
61 // running in the sign-in profile): | |
62 const char kTrivialExtensionId[] = "mockepjebcnmhmhcahfddgfcdgkdifnc"; | |
63 const char kTrivialExtensionUpdateManifestPath[] = | |
64 "extensions/trivial_extension/update_manifest.xml"; | |
65 | |
66 // Observer that allows waiting for an installation failure of a specific | |
67 // extension. | |
68 // TODO(emaxx): Extract this into a more generic helper class for using in other | |
69 // tests. | |
70 class ExtensionInstallErrorObserver final { | |
achuithb
2017/05/08 23:49:45
I haven't seen any other examples of final, but I
emaxx
2017/05/09 15:03:35
Yes, the wording is that it's actually "recommende
| |
71 public: | |
72 ExtensionInstallErrorObserver(const Profile* profile, | |
73 const std::string& extension_id) | |
74 : profile_(profile), | |
75 extension_id_(extension_id), | |
76 notification_observer_( | |
77 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR, | |
78 base::Bind(&ExtensionInstallErrorObserver::IsNotificationRelevant, | |
79 base::Unretained(this))) {} | |
achuithb
2017/05/08 23:49:45
I assume this use of 'this' in the initializer is
emaxx
2017/05/09 15:03:35
Yes, AFAIU it's equivalent to use of "this" in the
| |
80 | |
81 void Wait() { notification_observer_.Wait(); } | |
82 | |
83 private: | |
84 bool IsNotificationRelevant( | |
achuithb
2017/05/08 23:49:45
add comment?
emaxx
2017/05/09 15:03:35
Done.
| |
85 const content::NotificationSource& source, | |
86 const content::NotificationDetails& details) const { | |
87 extensions::CrxInstaller* const crx_installer = | |
88 content::Source<extensions::CrxInstaller>(source).ptr(); | |
89 return crx_installer->profile() == profile_ && | |
90 crx_installer->extension()->id() == extension_id_; | |
91 } | |
92 | |
93 const Profile* const profile_; | |
94 const std::string extension_id_; | |
95 content::WindowedNotificationObserver notification_observer_; | |
96 | |
97 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallErrorObserver); | |
98 }; | |
99 | |
100 // Observer that allows waiting until the background page of the specified | |
101 // extension loads. | |
102 // TODO(emaxx): Extract this into a more generic helper class for using in other | |
103 // tests. | |
104 class ExtensionBackgroundPageReadyObserver final { | |
105 public: | |
106 explicit ExtensionBackgroundPageReadyObserver(const std::string& extension_id) | |
107 : extension_id_(extension_id), | |
108 notification_observer_( | |
109 extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY, | |
110 base::Bind( | |
111 &ExtensionBackgroundPageReadyObserver::IsNotificationRelevant, | |
112 base::Unretained(this))) {} | |
113 | |
114 void Wait() { notification_observer_.Wait(); } | |
115 | |
116 private: | |
117 bool IsNotificationRelevant( | |
118 const content::NotificationSource& source, | |
119 const content::NotificationDetails& details) const { | |
120 return content::Source<const extensions::Extension>(source)->id() == | |
121 extension_id_; | |
122 } | |
123 | |
124 const std::string extension_id_; | |
125 content::WindowedNotificationObserver notification_observer_; | |
126 | |
127 DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver); | |
128 }; | |
129 | |
130 // Returns the initial profile, which is the original profile of the sign-in | |
131 // profile. The apps specified in the policy will be installed into the initial | |
132 // profile, and then become available in both. | |
133 Profile* GetProfile() { | |
134 // Intentionally not using the |chromeos::ProfileHelper::GetSigninProfile| | |
135 // method here, as it performs the lazy construction of the profile, while for | |
136 // the test purposes it's better to assert that it has been created before. | |
137 Profile* const profile = | |
138 g_browser_process->profile_manager()->GetProfileByPath( | |
139 chromeos::ProfileHelper::GetSigninProfileDir()); | |
140 DCHECK(profile); | |
141 return profile; | |
142 } | |
143 | |
21 // Tests for the sign-in profile apps being enabled via the command line flag. | 144 // Tests for the sign-in profile apps being enabled via the command line flag. |
22 // TODO(emaxx): Remove this smoke test once it's investigated whether just | 145 // TODO(emaxx): Remove this smoke test once it's investigated whether just |
23 // specifying this command line flag leads to tests being timed out. | 146 // specifying this command line flag leads to tests being timed out. |
24 class SigninProfileAppsEnabledViaCommandLineTest : public InProcessBrowserTest { | 147 class SigninProfileAppsEnabledViaCommandLineTest : public InProcessBrowserTest { |
25 protected: | 148 protected: |
26 SigninProfileAppsEnabledViaCommandLineTest() {} | 149 SigninProfileAppsEnabledViaCommandLineTest() {} |
27 | 150 |
28 void SetUpCommandLine(base::CommandLine* command_line) override { | 151 void SetUpCommandLine(base::CommandLine* command_line) override { |
29 InProcessBrowserTest::SetUpCommandLine(command_line); | 152 InProcessBrowserTest::SetUpCommandLine(command_line); |
30 command_line->AppendSwitch(switches::kEnableLoginScreenApps); | 153 command_line->AppendSwitch(switches::kEnableLoginScreenApps); |
31 } | 154 } |
32 | 155 |
33 private: | 156 private: |
34 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsEnabledViaCommandLineTest); | 157 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsEnabledViaCommandLineTest); |
35 }; | 158 }; |
36 | 159 |
37 } // namespace | 160 } // namespace |
38 | 161 |
39 IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest, | 162 IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest, |
40 NoExtensions) { | 163 NoExtensions) { |
41 Profile* const initial_profile = | 164 EXPECT_TRUE(extensions::ExtensionSystem::Get(GetProfile()) |
42 g_browser_process->profile_manager()->GetProfileByPath( | |
43 chromeos::ProfileHelper::GetSigninProfileDir()); | |
44 ASSERT_TRUE(initial_profile); | |
45 EXPECT_TRUE(extensions::ExtensionSystem::Get(initial_profile) | |
46 ->extension_service() | 165 ->extension_service() |
47 ->extensions_enabled()); | 166 ->extensions_enabled()); |
48 } | 167 } |
49 | 168 |
169 namespace { | |
170 | |
171 // Base class for testing sign-in profile apps that are installed via the device | |
172 // policy. | |
173 class SigninProfileAppsPolicyTestBase : public DevicePolicyCrosBrowserTest { | |
174 protected: | |
175 explicit SigninProfileAppsPolicyTestBase(version_info::Channel channel) | |
176 : channel_(channel), scoped_current_channel_(channel) {} | |
177 | |
178 void SetUpCommandLine(base::CommandLine* command_line) override { | |
179 DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line); | |
180 command_line->AppendSwitch(chromeos::switches::kLoginManager); | |
181 command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); | |
182 command_line->AppendSwitch(switches::kEnableLoginScreenApps); | |
183 } | |
184 | |
185 void SetUpInProcessBrowserTestFixture() override { | |
186 DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture(); | |
187 InstallOwnerKey(); | |
188 MarkAsEnterpriseOwned(); | |
189 } | |
190 | |
191 void SetUpOnMainThread() override { | |
192 EnableUrlRequestMocks(); | |
193 DevicePolicyCrosBrowserTest::SetUpOnMainThread(); | |
194 } | |
195 | |
196 void AddExtensionForForceInstallation( | |
197 const std::string& extension_id, | |
198 const base::FilePath& update_manifest_relative_path) { | |
199 const GURL update_manifest_url = net::URLRequestMockHTTPJob::GetMockUrl( | |
200 update_manifest_relative_path.MaybeAsASCII()); | |
201 const std::string policy_item_value = base::ReplaceStringPlaceholders( | |
202 "$1;$2", {extension_id, update_manifest_url.spec()}, nullptr); | |
203 device_policy() | |
204 ->payload() | |
205 .mutable_device_login_screen_app_install_list() | |
206 ->add_device_login_screen_app_install_list(policy_item_value); | |
207 RefreshDevicePolicy(); | |
208 } | |
209 | |
210 const version_info::Channel channel_; | |
211 | |
212 private: | |
213 // Enables URL request mocks for making the test data files (extensions' | |
214 // update manifests and packages) available under corresponding URLs. | |
215 static void EnableUrlRequestMocks() { | |
216 content::BrowserThread::PostTask( | |
217 content::BrowserThread::IO, FROM_HERE, | |
218 base::BindOnce(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); | |
219 } | |
220 | |
221 const extensions::ScopedCurrentChannel scoped_current_channel_; | |
222 | |
223 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTestBase); | |
224 }; | |
225 | |
226 // Class for testing sign-in profile apps under different browser channels. | |
227 class SigninProfileAppsPolicyPerChannelTest | |
228 : public SigninProfileAppsPolicyTestBase, | |
229 public testing::WithParamInterface<version_info::Channel> { | |
230 protected: | |
231 SigninProfileAppsPolicyPerChannelTest() | |
232 : SigninProfileAppsPolicyTestBase(GetParam()) {} | |
233 | |
234 private: | |
235 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyPerChannelTest); | |
236 }; | |
237 | |
238 } // namespace | |
239 | |
240 // Tests that a whitelisted app gets installed on any browser channel. | |
241 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, | |
242 WhitelistedAppInstallation) { | |
243 extensions::TestExtensionRegistryObserver registry_observer( | |
244 extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId); | |
245 | |
246 AddExtensionForForceInstallation(kTestAppId, | |
247 base::FilePath(kTestAppUpdateManifestPath)); | |
248 | |
249 registry_observer.WaitForExtensionLoaded(); | |
250 EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile()) | |
251 ->enabled_extensions() | |
252 .Contains(kTestAppId)); | |
253 } | |
254 | |
255 // Tests that a non-whitelisted app is installed only when on Dev, Canary or | |
256 // "unknown" (trunk) channels, but not on Beta or Stable channels. | |
257 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, | |
258 NotWhitelistedAppInstallation) { | |
259 extensions::TestExtensionRegistryObserver registry_observer( | |
260 extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId); | |
261 ExtensionInstallErrorObserver install_error_observer(GetProfile(), | |
262 kTrivialAppId); | |
263 | |
264 AddExtensionForForceInstallation( | |
265 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); | |
266 | |
267 switch (channel_) { | |
268 case version_info::Channel::UNKNOWN: | |
269 case version_info::Channel::CANARY: | |
270 case version_info::Channel::DEV: | |
271 registry_observer.WaitForExtensionLoaded(); | |
272 EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile()) | |
273 ->enabled_extensions() | |
274 .Contains(kTrivialAppId)); | |
275 break; | |
276 case version_info::Channel::BETA: | |
277 case version_info::Channel::STABLE: | |
278 install_error_observer.Wait(); | |
279 EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile()) | |
280 ->GetInstalledExtension(kTrivialAppId)); | |
281 break; | |
282 } | |
achuithb
2017/05/08 23:49:45
Should we add an assert for default?
emaxx
2017/05/09 15:03:35
Ack.
Note that in the current version, the compile
| |
283 } | |
284 | |
285 // Tests that an extension is forbidden from installation regardless of the | |
286 // browser channel. | |
287 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, | |
288 ExtensionInstallation) { | |
289 ExtensionInstallErrorObserver install_error_observer(GetProfile(), | |
290 kTrivialExtensionId); | |
291 | |
292 AddExtensionForForceInstallation( | |
293 kTrivialExtensionId, base::FilePath(kTrivialExtensionUpdateManifestPath)); | |
294 | |
295 install_error_observer.Wait(); | |
296 EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile()) | |
297 ->GetInstalledExtension(kTrivialExtensionId)); | |
298 } | |
299 | |
300 INSTANTIATE_TEST_CASE_P(, | |
301 SigninProfileAppsPolicyPerChannelTest, | |
302 testing::Values(version_info::Channel::UNKNOWN, | |
303 version_info::Channel::CANARY, | |
304 version_info::Channel::DEV, | |
305 version_info::Channel::BETA, | |
306 version_info::Channel::STABLE)); | |
307 | |
308 namespace { | |
309 | |
310 // Class for testing sign-in profile apps under the "unknown" browser channel, | |
311 // which allows to bypass the troublesome whitelist checks. | |
312 class SigninProfileAppsPolicyTest : public SigninProfileAppsPolicyTestBase { | |
313 protected: | |
314 SigninProfileAppsPolicyTest() | |
315 : SigninProfileAppsPolicyTestBase(version_info::Channel::UNKNOWN) {} | |
316 | |
317 private: | |
318 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTest); | |
319 }; | |
320 | |
321 } // namespace | |
322 | |
323 // Tests that a background page is created for the installed sign-in profile | |
324 // app. | |
325 IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, BackgroundPage) { | |
326 ExtensionBackgroundPageReadyObserver page_observer(kTrivialAppId); | |
327 AddExtensionForForceInstallation( | |
328 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); | |
329 page_observer.Wait(); | |
330 } | |
331 | |
332 // Tests installation of multiple sign-in profile apps. | |
333 IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, MultipleApps) { | |
334 extensions::TestExtensionRegistryObserver registry_observer1( | |
335 extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId); | |
336 extensions::TestExtensionRegistryObserver registry_observer2( | |
337 extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId); | |
338 | |
339 AddExtensionForForceInstallation(kTestAppId, | |
340 base::FilePath(kTestAppUpdateManifestPath)); | |
341 AddExtensionForForceInstallation( | |
342 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); | |
343 | |
344 registry_observer1.WaitForExtensionLoaded(); | |
345 registry_observer2.WaitForExtensionLoaded(); | |
346 } | |
347 | |
50 } // namespace policy | 348 } // namespace policy |
OLD | NEW |