| Index: chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
|
| diff --git a/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc b/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
|
| index cf2a18b60dae5b7ac6d1f801dea41e319790cda1..aea97d5349e1c62b0af32a189d9e6cc4e909f41a 100644
|
| --- a/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
|
| +++ b/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
|
| @@ -2,22 +2,149 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <string>
|
| +
|
| +#include "base/bind.h"
|
| #include "base/command_line.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| #include "base/macros.h"
|
| +#include "base/strings/string_util.h"
|
| #include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/chromeos/policy/device_policy_builder.h"
|
| +#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
|
| +#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
|
| #include "chrome/browser/chromeos/profiles/profile_helper.h"
|
| +#include "chrome/browser/extensions/crx_installer.h"
|
| #include "chrome/browser/extensions/extension_service.h"
|
| +#include "chrome/browser/net/url_request_mock_util.h"
|
| #include "chrome/browser/profiles/profile_manager.h"
|
| #include "chrome/common/chrome_switches.h"
|
| #include "chrome/test/base/in_process_browser_test.h"
|
| +#include "chromeos/chromeos_switches.h"
|
| +#include "chromeos/dbus/fake_session_manager_client.h"
|
| +#include "components/version_info/channel.h"
|
| +#include "components/version_info/version_info.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/notification_source.h"
|
| #include "content/public/test/browser_test.h"
|
| +#include "content/public/test/test_utils.h"
|
| +#include "extensions/browser/extension_registry.h"
|
| #include "extensions/browser/extension_system.h"
|
| +#include "extensions/browser/notification_types.h"
|
| +#include "extensions/browser/test_extension_registry_observer.h"
|
| +#include "extensions/common/extension.h"
|
| +#include "extensions/common/extension_set.h"
|
| +#include "extensions/common/features/feature_channel.h"
|
| +#include "net/test/url_request/url_request_mock_http_job.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
| +#include "url/gurl.h"
|
|
|
| namespace policy {
|
|
|
| namespace {
|
|
|
| +// Parameters for the several extensions and apps that are used by the tests in
|
| +// this file (note that the paths are given relative to the src/chrome/test/data
|
| +// directory):
|
| +// * The test app which is whitelisted for running in the sign-in profile:
|
| +const char kTestAppId[] = "bjaiihebfngildkcjkjckolinodhliff";
|
| +const char kTestAppUpdateManifestPath[] =
|
| + "extensions/signin_screen_test_app/update_manifest.xml";
|
| +// * A trivial test app which is NOT whitelisted for running in the sign-in
|
| +// profile:
|
| +const char kTrivialAppId[] = "mockapnacjbcdncmpkjngjalkhphojek";
|
| +const char kTrivialAppUpdateManifestPath[] =
|
| + "extensions/trivial_platform_app/update_manifest.xml";
|
| +// * A trivial test extension (note that extensions cannot be whitelisted for
|
| +// running in the sign-in profile):
|
| +const char kTrivialExtensionId[] = "mockepjebcnmhmhcahfddgfcdgkdifnc";
|
| +const char kTrivialExtensionUpdateManifestPath[] =
|
| + "extensions/trivial_extension/update_manifest.xml";
|
| +
|
| +// Observer that allows waiting for an installation failure of a specific
|
| +// extension/app.
|
| +// TODO(emaxx): Extract this into a more generic helper class for using in other
|
| +// tests.
|
| +class ExtensionInstallErrorObserver final {
|
| + public:
|
| + ExtensionInstallErrorObserver(const Profile* profile,
|
| + const std::string& extension_id)
|
| + : profile_(profile),
|
| + extension_id_(extension_id),
|
| + notification_observer_(
|
| + extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
|
| + base::Bind(&ExtensionInstallErrorObserver::IsNotificationRelevant,
|
| + base::Unretained(this))) {}
|
| +
|
| + void Wait() { notification_observer_.Wait(); }
|
| +
|
| + private:
|
| + // Callback which is used for |WindowedNotificationObserver| for checking
|
| + // whether the condition being awaited is met.
|
| + bool IsNotificationRelevant(
|
| + const content::NotificationSource& source,
|
| + const content::NotificationDetails& details) const {
|
| + extensions::CrxInstaller* const crx_installer =
|
| + content::Source<extensions::CrxInstaller>(source).ptr();
|
| + return crx_installer->profile() == profile_ &&
|
| + crx_installer->extension()->id() == extension_id_;
|
| + }
|
| +
|
| + const Profile* const profile_;
|
| + const std::string extension_id_;
|
| + content::WindowedNotificationObserver notification_observer_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ExtensionInstallErrorObserver);
|
| +};
|
| +
|
| +// Observer that allows waiting until the background page of the specified
|
| +// extension/app loads.
|
| +// TODO(emaxx): Extract this into a more generic helper class for using in other
|
| +// tests.
|
| +class ExtensionBackgroundPageReadyObserver final {
|
| + public:
|
| + explicit ExtensionBackgroundPageReadyObserver(const std::string& extension_id)
|
| + : extension_id_(extension_id),
|
| + notification_observer_(
|
| + extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
|
| + base::Bind(
|
| + &ExtensionBackgroundPageReadyObserver::IsNotificationRelevant,
|
| + base::Unretained(this))) {}
|
| +
|
| + void Wait() { notification_observer_.Wait(); }
|
| +
|
| + private:
|
| + // Callback which is used for |WindowedNotificationObserver| for checking
|
| + // whether the condition being awaited is met.
|
| + bool IsNotificationRelevant(
|
| + const content::NotificationSource& source,
|
| + const content::NotificationDetails& details) const {
|
| + return content::Source<const extensions::Extension>(source)->id() ==
|
| + extension_id_;
|
| + }
|
| +
|
| + const std::string extension_id_;
|
| + content::WindowedNotificationObserver notification_observer_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver);
|
| +};
|
| +
|
| +// Returns the initial profile, which is the original profile of the sign-in
|
| +// profile. The apps specified in the policy will be installed into the initial
|
| +// profile, and then become available in both.
|
| +Profile* GetProfile() {
|
| + // Intentionally not using the |chromeos::ProfileHelper::GetSigninProfile|
|
| + // method here, as it performs the lazy construction of the profile, while for
|
| + // the testing purposes it's better to assert that it has been created before.
|
| + Profile* const profile =
|
| + g_browser_process->profile_manager()->GetProfileByPath(
|
| + chromeos::ProfileHelper::GetSigninProfileDir());
|
| + DCHECK(profile);
|
| + return profile;
|
| +}
|
| +
|
| // Tests for the sign-in profile apps being enabled via the command line flag.
|
| // TODO(emaxx): Remove this smoke test once it's investigated whether just
|
| // specifying this command line flag leads to tests being timed out.
|
| @@ -38,13 +165,189 @@ class SigninProfileAppsEnabledViaCommandLineTest : public InProcessBrowserTest {
|
|
|
| IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest,
|
| NoExtensions) {
|
| - Profile* const initial_profile =
|
| - g_browser_process->profile_manager()->GetProfileByPath(
|
| - chromeos::ProfileHelper::GetSigninProfileDir());
|
| - ASSERT_TRUE(initial_profile);
|
| - EXPECT_TRUE(extensions::ExtensionSystem::Get(initial_profile)
|
| + EXPECT_TRUE(extensions::ExtensionSystem::Get(GetProfile())
|
| ->extension_service()
|
| ->extensions_enabled());
|
| }
|
|
|
| +namespace {
|
| +
|
| +// Base class for testing sign-in profile apps that are installed via the device
|
| +// policy.
|
| +class SigninProfileAppsPolicyTestBase : public DevicePolicyCrosBrowserTest {
|
| + protected:
|
| + explicit SigninProfileAppsPolicyTestBase(version_info::Channel channel)
|
| + : channel_(channel), scoped_current_channel_(channel) {}
|
| +
|
| + void SetUpCommandLine(base::CommandLine* command_line) override {
|
| + DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
|
| + command_line->AppendSwitch(chromeos::switches::kLoginManager);
|
| + command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
|
| + command_line->AppendSwitch(switches::kEnableLoginScreenApps);
|
| + }
|
| +
|
| + void SetUpInProcessBrowserTestFixture() override {
|
| + DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
|
| + InstallOwnerKey();
|
| + MarkAsEnterpriseOwned();
|
| + }
|
| +
|
| + void SetUpOnMainThread() override {
|
| + EnableUrlRequestMocks();
|
| + DevicePolicyCrosBrowserTest::SetUpOnMainThread();
|
| + }
|
| +
|
| + void AddExtensionForForceInstallation(
|
| + const std::string& extension_id,
|
| + const base::FilePath& update_manifest_relative_path) {
|
| + const GURL update_manifest_url = net::URLRequestMockHTTPJob::GetMockUrl(
|
| + update_manifest_relative_path.MaybeAsASCII());
|
| + const std::string policy_item_value = base::ReplaceStringPlaceholders(
|
| + "$1;$2", {extension_id, update_manifest_url.spec()}, nullptr);
|
| + device_policy()
|
| + ->payload()
|
| + .mutable_device_login_screen_app_install_list()
|
| + ->add_device_login_screen_app_install_list(policy_item_value);
|
| + RefreshDevicePolicy();
|
| + }
|
| +
|
| + const version_info::Channel channel_;
|
| +
|
| + private:
|
| + // Enables URL request mocks for making the test data files (extensions'
|
| + // update manifests and packages) available under corresponding URLs.
|
| + static void EnableUrlRequestMocks() {
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
|
| + }
|
| +
|
| + const extensions::ScopedCurrentChannel scoped_current_channel_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTestBase);
|
| +};
|
| +
|
| +// Class for testing sign-in profile apps under different browser channels.
|
| +class SigninProfileAppsPolicyPerChannelTest
|
| + : public SigninProfileAppsPolicyTestBase,
|
| + public testing::WithParamInterface<version_info::Channel> {
|
| + protected:
|
| + SigninProfileAppsPolicyPerChannelTest()
|
| + : SigninProfileAppsPolicyTestBase(GetParam()) {}
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyPerChannelTest);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// Tests that a whitelisted app gets installed on any browser channel.
|
| +IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
|
| + WhitelistedAppInstallation) {
|
| + extensions::TestExtensionRegistryObserver registry_observer(
|
| + extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId);
|
| +
|
| + AddExtensionForForceInstallation(kTestAppId,
|
| + base::FilePath(kTestAppUpdateManifestPath));
|
| +
|
| + registry_observer.WaitForExtensionLoaded();
|
| + EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile())
|
| + ->enabled_extensions()
|
| + .Contains(kTestAppId));
|
| +}
|
| +
|
| +// Tests that a non-whitelisted app is installed only when on Dev, Canary or
|
| +// "unknown" (trunk) channels, but not on Beta or Stable channels.
|
| +IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
|
| + NotWhitelistedAppInstallation) {
|
| + extensions::TestExtensionRegistryObserver registry_observer(
|
| + extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId);
|
| + ExtensionInstallErrorObserver install_error_observer(GetProfile(),
|
| + kTrivialAppId);
|
| +
|
| + AddExtensionForForceInstallation(
|
| + kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
|
| +
|
| + switch (channel_) {
|
| + case version_info::Channel::UNKNOWN:
|
| + case version_info::Channel::CANARY:
|
| + case version_info::Channel::DEV:
|
| + registry_observer.WaitForExtensionLoaded();
|
| + EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile())
|
| + ->enabled_extensions()
|
| + .Contains(kTrivialAppId));
|
| + break;
|
| + case version_info::Channel::BETA:
|
| + case version_info::Channel::STABLE:
|
| + install_error_observer.Wait();
|
| + EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile())
|
| + ->GetInstalledExtension(kTrivialAppId));
|
| + break;
|
| + }
|
| +}
|
| +
|
| +// Tests that an extension (as opposed to an app) is forbidden from installation
|
| +// regardless of the browser channel.
|
| +IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
|
| + ExtensionInstallation) {
|
| + ExtensionInstallErrorObserver install_error_observer(GetProfile(),
|
| + kTrivialExtensionId);
|
| +
|
| + AddExtensionForForceInstallation(
|
| + kTrivialExtensionId, base::FilePath(kTrivialExtensionUpdateManifestPath));
|
| +
|
| + install_error_observer.Wait();
|
| + EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile())
|
| + ->GetInstalledExtension(kTrivialExtensionId));
|
| +}
|
| +
|
| +// TODO(emaxx): Add the STABLE option once the scoped feature channel bug is
|
| +// fixed (https://crrev.com/2854293003).
|
| +INSTANTIATE_TEST_CASE_P(,
|
| + SigninProfileAppsPolicyPerChannelTest,
|
| + testing::Values(version_info::Channel::UNKNOWN,
|
| + version_info::Channel::CANARY,
|
| + version_info::Channel::DEV,
|
| + version_info::Channel::BETA));
|
| +
|
| +namespace {
|
| +
|
| +// Class for testing sign-in profile apps under the "unknown" browser channel,
|
| +// which allows to bypass the troublesome whitelist checks.
|
| +class SigninProfileAppsPolicyTest : public SigninProfileAppsPolicyTestBase {
|
| + protected:
|
| + SigninProfileAppsPolicyTest()
|
| + : SigninProfileAppsPolicyTestBase(version_info::Channel::UNKNOWN) {}
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTest);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// Tests that a background page is created for the installed sign-in profile
|
| +// app.
|
| +IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, BackgroundPage) {
|
| + ExtensionBackgroundPageReadyObserver page_observer(kTrivialAppId);
|
| + AddExtensionForForceInstallation(
|
| + kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
|
| + page_observer.Wait();
|
| +}
|
| +
|
| +// Tests installation of multiple sign-in profile apps.
|
| +IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, MultipleApps) {
|
| + extensions::TestExtensionRegistryObserver registry_observer1(
|
| + extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId);
|
| + extensions::TestExtensionRegistryObserver registry_observer2(
|
| + extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId);
|
| +
|
| + AddExtensionForForceInstallation(kTestAppId,
|
| + base::FilePath(kTestAppUpdateManifestPath));
|
| + AddExtensionForForceInstallation(
|
| + kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
|
| +
|
| + registry_observer1.WaitForExtensionLoaded();
|
| + registry_observer2.WaitForExtensionLoaded();
|
| +}
|
| +
|
| } // namespace policy
|
|
|