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/app. |
| 68 // TODO(emaxx): Extract this into a more generic helper class for using in other |
| 69 // tests. |
| 70 class ExtensionInstallErrorObserver final { |
| 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))) {} |
| 80 |
| 81 void Wait() { notification_observer_.Wait(); } |
| 82 |
| 83 private: |
| 84 // Callback which is used for |WindowedNotificationObserver| for checking |
| 85 // whether the condition being awaited is met. |
| 86 bool IsNotificationRelevant( |
| 87 const content::NotificationSource& source, |
| 88 const content::NotificationDetails& details) const { |
| 89 extensions::CrxInstaller* const crx_installer = |
| 90 content::Source<extensions::CrxInstaller>(source).ptr(); |
| 91 return crx_installer->profile() == profile_ && |
| 92 crx_installer->extension()->id() == extension_id_; |
| 93 } |
| 94 |
| 95 const Profile* const profile_; |
| 96 const std::string extension_id_; |
| 97 content::WindowedNotificationObserver notification_observer_; |
| 98 |
| 99 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallErrorObserver); |
| 100 }; |
| 101 |
| 102 // Observer that allows waiting until the background page of the specified |
| 103 // extension/app loads. |
| 104 // TODO(emaxx): Extract this into a more generic helper class for using in other |
| 105 // tests. |
| 106 class ExtensionBackgroundPageReadyObserver final { |
| 107 public: |
| 108 explicit ExtensionBackgroundPageReadyObserver(const std::string& extension_id) |
| 109 : extension_id_(extension_id), |
| 110 notification_observer_( |
| 111 extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY, |
| 112 base::Bind( |
| 113 &ExtensionBackgroundPageReadyObserver::IsNotificationRelevant, |
| 114 base::Unretained(this))) {} |
| 115 |
| 116 void Wait() { notification_observer_.Wait(); } |
| 117 |
| 118 private: |
| 119 // Callback which is used for |WindowedNotificationObserver| for checking |
| 120 // whether the condition being awaited is met. |
| 121 bool IsNotificationRelevant( |
| 122 const content::NotificationSource& source, |
| 123 const content::NotificationDetails& details) const { |
| 124 return content::Source<const extensions::Extension>(source)->id() == |
| 125 extension_id_; |
| 126 } |
| 127 |
| 128 const std::string extension_id_; |
| 129 content::WindowedNotificationObserver notification_observer_; |
| 130 |
| 131 DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver); |
| 132 }; |
| 133 |
| 134 // Returns the initial profile, which is the original profile of the sign-in |
| 135 // profile. The apps specified in the policy will be installed into the initial |
| 136 // profile, and then become available in both. |
| 137 Profile* GetProfile() { |
| 138 // Intentionally not using the |chromeos::ProfileHelper::GetSigninProfile| |
| 139 // method here, as it performs the lazy construction of the profile, while for |
| 140 // the testing purposes it's better to assert that it has been created before. |
| 141 Profile* const profile = |
| 142 g_browser_process->profile_manager()->GetProfileByPath( |
| 143 chromeos::ProfileHelper::GetSigninProfileDir()); |
| 144 DCHECK(profile); |
| 145 return profile; |
| 146 } |
| 147 |
21 // Tests for the sign-in profile apps being enabled via the command line flag. | 148 // 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 | 149 // TODO(emaxx): Remove this smoke test once it's investigated whether just |
23 // specifying this command line flag leads to tests being timed out. | 150 // specifying this command line flag leads to tests being timed out. |
24 class SigninProfileAppsEnabledViaCommandLineTest : public InProcessBrowserTest { | 151 class SigninProfileAppsEnabledViaCommandLineTest : public InProcessBrowserTest { |
25 protected: | 152 protected: |
26 SigninProfileAppsEnabledViaCommandLineTest() {} | 153 SigninProfileAppsEnabledViaCommandLineTest() {} |
27 | 154 |
28 void SetUpCommandLine(base::CommandLine* command_line) override { | 155 void SetUpCommandLine(base::CommandLine* command_line) override { |
29 InProcessBrowserTest::SetUpCommandLine(command_line); | 156 InProcessBrowserTest::SetUpCommandLine(command_line); |
30 command_line->AppendSwitch(switches::kEnableLoginScreenApps); | 157 command_line->AppendSwitch(switches::kEnableLoginScreenApps); |
31 } | 158 } |
32 | 159 |
33 private: | 160 private: |
34 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsEnabledViaCommandLineTest); | 161 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsEnabledViaCommandLineTest); |
35 }; | 162 }; |
36 | 163 |
37 } // namespace | 164 } // namespace |
38 | 165 |
39 IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest, | 166 IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest, |
40 NoExtensions) { | 167 NoExtensions) { |
41 Profile* const initial_profile = | 168 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() | 169 ->extension_service() |
47 ->extensions_enabled()); | 170 ->extensions_enabled()); |
48 } | 171 } |
49 | 172 |
| 173 namespace { |
| 174 |
| 175 // Base class for testing sign-in profile apps that are installed via the device |
| 176 // policy. |
| 177 class SigninProfileAppsPolicyTestBase : public DevicePolicyCrosBrowserTest { |
| 178 protected: |
| 179 explicit SigninProfileAppsPolicyTestBase(version_info::Channel channel) |
| 180 : channel_(channel), scoped_current_channel_(channel) {} |
| 181 |
| 182 void SetUpCommandLine(base::CommandLine* command_line) override { |
| 183 DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line); |
| 184 command_line->AppendSwitch(chromeos::switches::kLoginManager); |
| 185 command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); |
| 186 command_line->AppendSwitch(switches::kEnableLoginScreenApps); |
| 187 } |
| 188 |
| 189 void SetUpInProcessBrowserTestFixture() override { |
| 190 DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture(); |
| 191 InstallOwnerKey(); |
| 192 MarkAsEnterpriseOwned(); |
| 193 } |
| 194 |
| 195 void SetUpOnMainThread() override { |
| 196 EnableUrlRequestMocks(); |
| 197 DevicePolicyCrosBrowserTest::SetUpOnMainThread(); |
| 198 } |
| 199 |
| 200 void AddExtensionForForceInstallation( |
| 201 const std::string& extension_id, |
| 202 const base::FilePath& update_manifest_relative_path) { |
| 203 const GURL update_manifest_url = net::URLRequestMockHTTPJob::GetMockUrl( |
| 204 update_manifest_relative_path.MaybeAsASCII()); |
| 205 const std::string policy_item_value = base::ReplaceStringPlaceholders( |
| 206 "$1;$2", {extension_id, update_manifest_url.spec()}, nullptr); |
| 207 device_policy() |
| 208 ->payload() |
| 209 .mutable_device_login_screen_app_install_list() |
| 210 ->add_device_login_screen_app_install_list(policy_item_value); |
| 211 RefreshDevicePolicy(); |
| 212 } |
| 213 |
| 214 const version_info::Channel channel_; |
| 215 |
| 216 private: |
| 217 // Enables URL request mocks for making the test data files (extensions' |
| 218 // update manifests and packages) available under corresponding URLs. |
| 219 static void EnableUrlRequestMocks() { |
| 220 content::BrowserThread::PostTask( |
| 221 content::BrowserThread::IO, FROM_HERE, |
| 222 base::BindOnce(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); |
| 223 } |
| 224 |
| 225 const extensions::ScopedCurrentChannel scoped_current_channel_; |
| 226 |
| 227 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTestBase); |
| 228 }; |
| 229 |
| 230 // Class for testing sign-in profile apps under different browser channels. |
| 231 class SigninProfileAppsPolicyPerChannelTest |
| 232 : public SigninProfileAppsPolicyTestBase, |
| 233 public testing::WithParamInterface<version_info::Channel> { |
| 234 protected: |
| 235 SigninProfileAppsPolicyPerChannelTest() |
| 236 : SigninProfileAppsPolicyTestBase(GetParam()) {} |
| 237 |
| 238 private: |
| 239 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyPerChannelTest); |
| 240 }; |
| 241 |
| 242 } // namespace |
| 243 |
| 244 // Tests that a whitelisted app gets installed on any browser channel. |
| 245 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, |
| 246 WhitelistedAppInstallation) { |
| 247 extensions::TestExtensionRegistryObserver registry_observer( |
| 248 extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId); |
| 249 |
| 250 AddExtensionForForceInstallation(kTestAppId, |
| 251 base::FilePath(kTestAppUpdateManifestPath)); |
| 252 |
| 253 registry_observer.WaitForExtensionLoaded(); |
| 254 EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile()) |
| 255 ->enabled_extensions() |
| 256 .Contains(kTestAppId)); |
| 257 } |
| 258 |
| 259 // Tests that a non-whitelisted app is installed only when on Dev, Canary or |
| 260 // "unknown" (trunk) channels, but not on Beta or Stable channels. |
| 261 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, |
| 262 NotWhitelistedAppInstallation) { |
| 263 extensions::TestExtensionRegistryObserver registry_observer( |
| 264 extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId); |
| 265 ExtensionInstallErrorObserver install_error_observer(GetProfile(), |
| 266 kTrivialAppId); |
| 267 |
| 268 AddExtensionForForceInstallation( |
| 269 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); |
| 270 |
| 271 switch (channel_) { |
| 272 case version_info::Channel::UNKNOWN: |
| 273 case version_info::Channel::CANARY: |
| 274 case version_info::Channel::DEV: |
| 275 registry_observer.WaitForExtensionLoaded(); |
| 276 EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile()) |
| 277 ->enabled_extensions() |
| 278 .Contains(kTrivialAppId)); |
| 279 break; |
| 280 case version_info::Channel::BETA: |
| 281 case version_info::Channel::STABLE: |
| 282 install_error_observer.Wait(); |
| 283 EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile()) |
| 284 ->GetInstalledExtension(kTrivialAppId)); |
| 285 break; |
| 286 } |
| 287 } |
| 288 |
| 289 // Tests that an extension (as opposed to an app) is forbidden from installation |
| 290 // regardless of the browser channel. |
| 291 IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest, |
| 292 ExtensionInstallation) { |
| 293 ExtensionInstallErrorObserver install_error_observer(GetProfile(), |
| 294 kTrivialExtensionId); |
| 295 |
| 296 AddExtensionForForceInstallation( |
| 297 kTrivialExtensionId, base::FilePath(kTrivialExtensionUpdateManifestPath)); |
| 298 |
| 299 install_error_observer.Wait(); |
| 300 EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile()) |
| 301 ->GetInstalledExtension(kTrivialExtensionId)); |
| 302 } |
| 303 |
| 304 // TODO(emaxx): Add the STABLE option once the scoped feature channel bug is |
| 305 // fixed (https://crrev.com/2854293003). |
| 306 INSTANTIATE_TEST_CASE_P(, |
| 307 SigninProfileAppsPolicyPerChannelTest, |
| 308 testing::Values(version_info::Channel::UNKNOWN, |
| 309 version_info::Channel::CANARY, |
| 310 version_info::Channel::DEV, |
| 311 version_info::Channel::BETA)); |
| 312 |
| 313 namespace { |
| 314 |
| 315 // Class for testing sign-in profile apps under the "unknown" browser channel, |
| 316 // which allows to bypass the troublesome whitelist checks. |
| 317 class SigninProfileAppsPolicyTest : public SigninProfileAppsPolicyTestBase { |
| 318 protected: |
| 319 SigninProfileAppsPolicyTest() |
| 320 : SigninProfileAppsPolicyTestBase(version_info::Channel::UNKNOWN) {} |
| 321 |
| 322 private: |
| 323 DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTest); |
| 324 }; |
| 325 |
| 326 } // namespace |
| 327 |
| 328 // Tests that a background page is created for the installed sign-in profile |
| 329 // app. |
| 330 IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, BackgroundPage) { |
| 331 ExtensionBackgroundPageReadyObserver page_observer(kTrivialAppId); |
| 332 AddExtensionForForceInstallation( |
| 333 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); |
| 334 page_observer.Wait(); |
| 335 } |
| 336 |
| 337 // Tests installation of multiple sign-in profile apps. |
| 338 IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, MultipleApps) { |
| 339 extensions::TestExtensionRegistryObserver registry_observer1( |
| 340 extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId); |
| 341 extensions::TestExtensionRegistryObserver registry_observer2( |
| 342 extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId); |
| 343 |
| 344 AddExtensionForForceInstallation(kTestAppId, |
| 345 base::FilePath(kTestAppUpdateManifestPath)); |
| 346 AddExtensionForForceInstallation( |
| 347 kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath)); |
| 348 |
| 349 registry_observer1.WaitForExtensionLoaded(); |
| 350 registry_observer2.WaitForExtensionLoaded(); |
| 351 } |
| 352 |
50 } // namespace policy | 353 } // namespace policy |
OLD | NEW |