blob: 32df7509bf816eb635e245003a240f8e634f5aa2 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <ostream>
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/screens/gaia_view.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/chromeos/settings/stub_install_attributes.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_constants.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/switches/chrome_switches.h"
namespace em = enterprise_management;
namespace chromeos {
namespace {
struct Params {
Params(bool login_screen_site_per_process,
std::string login_screen_isolate_origins,
bool user_policy_site_per_process,
std::string user_policy_isolate_origins,
bool ephemeral_users,
bool expected_request_restart,
std::vector<std::string> expected_flags_for_user)
: login_screen_site_per_process(login_screen_site_per_process),
login_screen_isolate_origins(login_screen_isolate_origins),
user_policy_site_per_process(user_policy_site_per_process),
user_policy_isolate_origins(user_policy_isolate_origins),
ephemeral_users(ephemeral_users),
expected_request_restart(expected_request_restart),
expected_flags_for_user(expected_flags_for_user) {}
friend std::ostream& operator<<(std::ostream& os, const Params& p) {
os << "{" << std::endl
<< " login_screen_site_per_process: " << p.login_screen_site_per_process
<< std::endl
<< " login_screen_isolate_origins: " << p.login_screen_isolate_origins
<< std::endl
<< " user_policy_site_per_process: " << p.user_policy_site_per_process
<< std::endl
<< " user_policy_isolate_origins: " << p.user_policy_isolate_origins
<< std::endl
<< " ephemeral_users: " << p.ephemeral_users << std::endl
<< " expected_request_restart: " << p.expected_request_restart
<< std::endl
<< " expected_flags_for_user: "
<< base::JoinString(p.expected_flags_for_user, ", ") << std::endl
<< "}";
return os;
}
// If true, --site-per-process will be passed to the login manager chrome
// instance between policy flag sentinels.
// Note: On Chrome OS, login_manager evaluates device policy and does this.
bool login_screen_site_per_process;
// If non-empty, --isolate-origins=|login_screen_isolate_origins| will be
// passed to the login manager chrome instance between policy flag sentinels.
// Note: On Chrome OS, login_manager evaluates device policy and does this.
std::string login_screen_isolate_origins;
// If true, the SitePerProcess user policy will be simulated to be set to
// true.
bool user_policy_site_per_process;
// If non-empty, the IsolateOrigins user policy will be simulated to be set
// |user_policy_isolate_origins|.
std::string user_policy_isolate_origins;
// If true, ephemeral users are enabled.
bool ephemeral_users;
// If true, the test case will expect that AttemptRestart has been called by
// UserSessionManager.
bool expected_request_restart;
// When a restart was requested, the test case verifies that the flags passed
// to |SessionManagerClient::SetFlagsForUser| match
// |expected_flags_for_user|.
std::vector<std::string> expected_flags_for_user;
};
// Defines the test cases that will be executed.
const Params kTestCases[] = {
// No site isolation in device or user policy - no restart expected.
Params(false /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
false /* ephemeral_users */,
false /* expected_request_restart */,
{} /* expected_flags_for_user */),
// SitePerProcess in user policy only - restart expected with
// additional --site-per-process flag.
Params(false /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
true /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{"--policy-switches-begin", "--site-per-process",
"--policy-switches-end"} /* expected_flags_for_user */),
// SitePerProcess in device and user policy - no restart expected.
Params(true /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
true /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
false /* ephemeral_users */,
false /* expected_request_restart */,
{} /* expected_flags_for_user */),
// SitePerProcess only in device policy - restart expected.
Params(true /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{} /* expected_flags_for_user */),
// IsolateOrigins in user policy only - restart expected with
// additional --isolate-origins flag.
Params(false /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
"https://example.com" /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{"--policy-switches-begin", "--isolate-origins=https://example.com",
"--policy-switches-end"} /* expected_flags_for_user */),
// Equal IsolateOrigins in device and user policy - no restart expected.
Params(false /* login_screen_site_per_process */,
"https://example.com" /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
"https://example.com" /* user_policy_isolate_origins */,
false /* ephemeral_users */,
false /* expected_request_restart */,
{} /* expected_flags_for_user */),
// Different IsolateOrigins in device and user policy - restart expected.
Params(false /* login_screen_site_per_process */,
"https://example.com" /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
"https://example2.com" /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{"--policy-switches-begin", "--isolate-origins=https://example2.com",
"--policy-switches-end"} /* expected_flags_for_user */),
// IsolateOrigins only in device policy - restart expected.
Params(true /* login_screen_site_per_process */,
"https://example.com" /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{} /* expected_flags_for_user */),
// SitePerProcess in device policy, IsolateOrigins in user policy - restart
// expected.
Params(true /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
"https://example.com" /* user_policy_isolate_origins */,
false /* ephemeral_users */,
true /* expected_request_restart */,
{"--policy-switches-begin", "--isolate-origins=https://example.com",
"--policy-switches-end"} /* expected_flags_for_user */),
// With ephemeral users: No site isolation in device or user policy - no
// restart expected.
Params(false /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
false /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
true /* ephemeral_users */,
false /* expected_request_restart */,
{} /* expected_flags_for_user */),
// With ephemeral users: SitePerProcess in user policy only - restart
// expected with additional --site-per-process flag.
Params(false /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
true /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
true /* ephemeral_users */,
true /* expected_request_restart */,
{"--profile-requires-policy=true", "--policy-switches-begin",
"--site-per-process",
"--policy-switches-end"} /* expected_flags_for_user */),
// With ephemeral uses: SitePerProcess in device and user policy - no
// restart expected.
Params(true /* login_screen_site_per_process */,
std::string() /* login_screen_isolate_origins */,
true /* user_policy_site_per_process */,
std::string() /* user_policy_isolate_origins */,
true /* ephemeral_users */,
false /* expected_request_restart */,
{} /* expected_flags_for_user */)};
constexpr char kTestUserGaiaId[] = "1111111111";
class SiteIsolationFlagHandlingTest
: public policy::LoginPolicyTestBase,
public ::testing::WithParamInterface<Params> {
protected:
SiteIsolationFlagHandlingTest() : settings_helper_(false) {}
void SetUpCommandLine(base::CommandLine* command_line) override {
policy::LoginPolicyTestBase::SetUpCommandLine(command_line);
// Simulate login_manager behavior: pass --site-per-process or
// --isolate-origins between policy flag sentinels according to test case
// parameters.
bool use_policy_switches_sentinels =
GetParam().login_screen_site_per_process ||
!GetParam().login_screen_isolate_origins.empty();
if (use_policy_switches_sentinels)
command_line->AppendSwitch(switches::kPolicySwitchesBegin);
if (GetParam().login_screen_site_per_process)
command_line->AppendSwitch(::switches::kSitePerProcess);
if (!GetParam().login_screen_isolate_origins.empty()) {
command_line->AppendSwitchASCII(::switches::kIsolateOrigins,
GetParam().login_screen_isolate_origins);
}
if (use_policy_switches_sentinels)
command_line->AppendSwitch(switches::kPolicySwitchesEnd);
}
// This is called from |LoginPolicyTestBase| and specifies the user policy
// values to be served by the local policy test server.
void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const override {
if (GetParam().user_policy_site_per_process) {
policy->Set(policy::key::kSitePerProcess,
std::make_unique<base::Value>(true));
}
if (!GetParam().user_policy_isolate_origins.empty()) {
policy->Set(policy::key::kIsolateOrigins,
std::make_unique<base::Value>(
GetParam().user_policy_isolate_origins));
}
}
void SetUpInProcessBrowserTestFixture() override {
policy::LoginPolicyTestBase::SetUpInProcessBrowserTestFixture();
// Set up fake_session_manager_client_ so we can verify the flags for the
// user session.
auto fake_session_manager_client =
std::make_unique<FakeSessionManagerClient>(
FakeSessionManagerClient::PolicyStorageType::kOnDisk);
fake_session_manager_client_ = fake_session_manager_client.get();
DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
std::move(fake_session_manager_client));
// Mark that chrome restart can be requested.
// Note that AttemptRestart() is mocked out in UserSessionManager through
// |SetAttemptRestartClosureInTests| (set up in SetUpOnMainThread).
fake_session_manager_client_->set_supports_restart_to_apply_user_flags(
true);
}
void SetUpOnMainThread() override {
policy::LoginPolicyTestBase::SetUpOnMainThread();
// Write ephemeral users status directly into CrosSettings.
settings_helper_.ReplaceProvider(kAccountsPrefEphemeralUsersEnabled);
settings_helper_.SetBoolean(kAccountsPrefEphemeralUsersEnabled,
GetParam().ephemeral_users);
// This makes the user manager reload CrosSettings.
GetChromeUserManager()->OwnershipStatusChanged();
login_wait_loop_ = std::make_unique<base::RunLoop>();
// Mock out chrome restart.
test::UserSessionManagerTestApi session_manager_test_api(
UserSessionManager::GetInstance());
session_manager_test_api.SetAttemptRestartClosureInTests(
base::BindRepeating(
&SiteIsolationFlagHandlingTest::AttemptRestartCalled,
base::Unretained(this)));
// Observe for user session start.
user_session_started_observer_ =
std::make_unique<content::WindowedNotificationObserver>(
chrome::NOTIFICATION_SESSION_STARTED,
base::BindRepeating(
&SiteIsolationFlagHandlingTest::UserSessionStarted,
base::Unretained(this)));
}
void TearDownOnMainThread() override { settings_helper_.RestoreProvider(); }
ChromeUserManagerImpl* GetChromeUserManager() const {
return static_cast<ChromeUserManagerImpl*>(
user_manager::UserManager::Get());
}
bool HasAttemptRestartBeenCalled() const { return attempt_restart_called_; }
FakeSessionManagerClient* fake_session_manager_client() {
return fake_session_manager_client_;
}
// Called when log-in was successful.
bool UserSessionStarted(const content::NotificationSource& source,
const content::NotificationDetails& details) {
login_wait_loop_->Quit();
return true;
}
// Used to wait until either login succeeds by starting a user session, or
// chrome requests a restart.
std::unique_ptr<base::RunLoop> login_wait_loop_;
private:
// Called when chrome requests a restarted.
void AttemptRestartCalled() {
login_wait_loop_->Quit();
attempt_restart_called_ = true;
}
// This will be set to |true| when chrome has requested a restart.
bool attempt_restart_called_ = false;
// Set up fake install attributes to pretend the machine is enrolled. This
// is important because ephemeral users only work on enrolled machines.
ScopedStubInstallAttributes test_install_attributes_{
StubInstallAttributes::CreateCloudManaged("example.com", "fake-id")};
// Observes for user session start.
std::unique_ptr<content::WindowedNotificationObserver>
user_session_started_observer_;
// Unowned pointer - owned by DBusThreadManager.
FakeSessionManagerClient* fake_session_manager_client_;
policy::MockConfigurationPolicyProvider provider_;
ScopedCrosSettingsTestHelper settings_helper_;
DISALLOW_COPY_AND_ASSIGN(SiteIsolationFlagHandlingTest);
};
} // namespace
IN_PROC_BROWSER_TEST_P(SiteIsolationFlagHandlingTest, FlagHandlingTest) {
// Start user sign-in. We can't use |LoginPolicyTestBase::LogIn|, because
// it waits for a user session start unconditionally, which will not happen if
// chrome requests a restart to set user-session flags.
SkipToLoginScreen();
LoginDisplayHost::default_host()
->GetOobeUI()
->GetGaiaScreenView()
->ShowSigninScreenForTest(kAccountId, kAccountPassword, kEmptyServices);
// Wait for either the user session to start, or for restart to be requested
// (whichever happens first).
login_wait_loop_->Run();
EXPECT_EQ(GetParam().expected_request_restart, HasAttemptRestartBeenCalled());
if (!HasAttemptRestartBeenCalled())
return;
// Also verify flags if chrome was restarted.
AccountId test_account_id =
AccountId::FromUserEmailGaiaId(GetAccount(), kTestUserGaiaId);
std::vector<std::string> flags_for_user;
bool has_flags_for_user = fake_session_manager_client()->GetFlagsForUser(
cryptohome::CreateAccountIdentifierFromAccountId(test_account_id),
&flags_for_user);
EXPECT_TRUE(has_flags_for_user);
// Remove flag sentinels. Keep whatever is between those sentinels, to
// verify that we don't pass additional parameters in there.
base::EraseIf(flags_for_user, [](const std::string& flag) {
return flag == "--flag-switches-begin" || flag == "--flag-switches-end";
});
EXPECT_EQ(GetParam().expected_flags_for_user, flags_for_user);
}
INSTANTIATE_TEST_CASE_P(,
SiteIsolationFlagHandlingTest,
::testing::ValuesIn(kTestCases));
} // namespace chromeos