Chromium Code Reviews| 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 |