blob: 14fc1c5705e5da82beb4245f44d91728f0e435df [file] [log] [blame]
// Copyright 2013 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 <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_writer.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.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/test/login_manager_mixin.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/constants/dbus_paths.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/dbus/session_manager/session_manager_client.h"
#include "chromeos/login/auth/user_context.h"
#include "components/account_id/account_id.h"
#include "components/policy/core/common/cloud/policy_builder.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/test_launcher.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
// Use consumer.example.com to keep policy code out of the tests.
constexpr char kUserId1[] = "user1@consumer.example.com";
constexpr char kUserId2[] = "user2@consumer.example.com";
constexpr char kUserId3[] = "user3@consumer.example.com";
} // namespace
class CrashRestoreSimpleTest : public InProcessBrowserTest {
protected:
CrashRestoreSimpleTest() {}
~CrashRestoreSimpleTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kLoginUser,
cryptohome_id1_.account_id());
command_line->AppendSwitchASCII(
switches::kLoginProfile,
CryptohomeClient::GetStubSanitizedUsername(cryptohome_id1_));
}
void SetUpInProcessBrowserTestFixture() override {
// Override FakeSessionManagerClient. This will be shut down by the browser.
SessionManagerClient::InitializeFakeInMemory();
FakeSessionManagerClient::Get()->StartSession(cryptohome_id1_);
}
const AccountId account_id1_ = AccountId::FromUserEmail(kUserId1);
const AccountId account_id2_ = AccountId::FromUserEmail(kUserId2);
const AccountId account_id3_ = AccountId::FromUserEmail(kUserId3);
const cryptohome::AccountIdentifier cryptohome_id1_ =
cryptohome::CreateAccountIdentifierFromAccountId(account_id1_);
const cryptohome::AccountIdentifier cryptohome_id2_ =
cryptohome::CreateAccountIdentifierFromAccountId(account_id2_);
const cryptohome::AccountIdentifier cryptohome_id3_ =
cryptohome::CreateAccountIdentifierFromAccountId(account_id3_);
};
IN_PROC_BROWSER_TEST_F(CrashRestoreSimpleTest, RestoreSessionForOneUser) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
user_manager::User* user = user_manager->GetActiveUser();
ASSERT_TRUE(user);
EXPECT_EQ(account_id1_, user->GetAccountId());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(cryptohome_id1_),
user->username_hash());
EXPECT_EQ(1UL, user_manager->GetLoggedInUsers().size());
auto* session_manager = session_manager::SessionManager::Get();
EXPECT_EQ(session_manager::SessionState::ACTIVE,
session_manager->session_state());
EXPECT_EQ(1u, session_manager->sessions().size());
}
// Observer that keeps track of user sessions restore event.
class UserSessionRestoreObserver : public UserSessionStateObserver {
public:
UserSessionRestoreObserver()
: running_loop_(false),
user_sessions_restored_(
UserSessionManager::GetInstance()->UserSessionsRestored()) {
if (!user_sessions_restored_)
UserSessionManager::GetInstance()->AddSessionStateObserver(this);
}
~UserSessionRestoreObserver() override {}
void PendingUserSessionsRestoreFinished() override {
user_sessions_restored_ = true;
UserSessionManager::GetInstance()->RemoveSessionStateObserver(this);
if (!running_loop_)
return;
message_loop_runner_->Quit();
running_loop_ = false;
}
// Wait until the user sessions are restored. If that happened between the
// construction of this object and this call or even before it was created
// then it returns immediately.
void Wait() {
if (user_sessions_restored_)
return;
running_loop_ = true;
message_loop_runner_ = new content::MessageLoopRunner();
message_loop_runner_->Run();
}
private:
bool running_loop_;
bool user_sessions_restored_;
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(UserSessionRestoreObserver);
};
class CrashRestoreComplexTest : public CrashRestoreSimpleTest {
protected:
CrashRestoreComplexTest() {}
~CrashRestoreComplexTest() override {}
bool SetUpUserDataDirectory() override {
RegisterUsers();
CreateUserProfiles();
return true;
}
void SetUpInProcessBrowserTestFixture() override {
CrashRestoreSimpleTest::SetUpInProcessBrowserTestFixture();
FakeSessionManagerClient::Get()->StartSession(cryptohome_id2_);
FakeSessionManagerClient::Get()->StartSession(cryptohome_id3_);
}
// Register test users so that UserManager knows them and make kUserId3 as the
// last active user.
void RegisterUsers() {
base::DictionaryValue local_state;
const char* kTestUserIds[] = {kUserId1, kUserId2, kUserId3};
auto users_list = std::make_unique<base::ListValue>();
for (auto* user_id : kTestUserIds)
users_list->AppendString(user_id);
local_state.SetList("LoggedInUsers", std::move(users_list));
local_state.SetString("LastActiveUser", kUserId3);
auto known_users_list = std::make_unique<base::ListValue>();
int gaia_id = 10000;
for (auto* user_id : kTestUserIds) {
auto user_dict = std::make_unique<base::DictionaryValue>();
user_dict->SetString("account_type", "google");
user_dict->SetString("email", user_id);
user_dict->SetString("gaia_id", base::NumberToString(gaia_id++));
known_users_list->Append(std::move(user_dict));
}
local_state.SetList("KnownUsers", std::move(known_users_list));
std::string local_state_json;
ASSERT_TRUE(base::JSONWriter::Write(local_state, &local_state_json));
base::FilePath local_state_file;
ASSERT_TRUE(
base::PathService::Get(chrome::DIR_USER_DATA, &local_state_file));
local_state_file = local_state_file.Append(chrome::kLocalStateFilename);
ASSERT_NE(-1, base::WriteFile(local_state_file, local_state_json.data(),
local_state_json.size()));
}
// Creates user profiles with open user sessions to simulate crashes.
void CreateUserProfiles() {
base::DictionaryValue prefs;
prefs.SetString(prefs::kSessionExitType, "Crashed");
std::string prefs_json;
ASSERT_TRUE(base::JSONWriter::Write(prefs, &prefs_json));
base::FilePath user_data_dir;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
const char* kTestUserIds[] = {kUserId1, kUserId2, kUserId3};
for (auto* user_id : kTestUserIds) {
const std::string user_id_hash =
ProfileHelper::GetUserIdHashByUserIdForTesting(user_id);
const base::FilePath user_profile_path =
user_data_dir.Append(ProfileHelper::GetUserProfileDir(user_id_hash));
ASSERT_TRUE(base::CreateDirectory(user_profile_path));
ASSERT_NE(-1, base::WriteFile(user_profile_path.Append("Preferences"),
prefs_json.data(), prefs_json.size()));
}
}
};
IN_PROC_BROWSER_TEST_F(CrashRestoreComplexTest, RestoreSessionForThreeUsers) {
{
UserSessionRestoreObserver restore_observer;
restore_observer.Wait();
}
chromeos::test::UserSessionManagerTestApi session_manager_test_api(
chromeos::UserSessionManager::GetInstance());
session_manager_test_api.SetShouldObtainTokenHandleInTests(false);
DCHECK(UserSessionManager::GetInstance()->UserSessionsRestored());
// User that is last in the user sessions map becomes active. This behavior
// will become better defined once each user gets a separate user desktop.
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
user_manager::User* user = user_manager->GetActiveUser();
ASSERT_TRUE(user);
EXPECT_EQ(account_id3_, user->GetAccountId());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(cryptohome_id3_),
user->username_hash());
const user_manager::UserList& users = user_manager->GetLRULoggedInUsers();
ASSERT_EQ(3UL, users.size());
// User that becomes active moves to the beginning of the list.
EXPECT_EQ(account_id3_, users[0]->GetAccountId());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(cryptohome_id3_),
users[0]->username_hash());
EXPECT_EQ(account_id1_, users[1]->GetAccountId());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(cryptohome_id1_),
users[1]->username_hash());
EXPECT_EQ(account_id2_, users[2]->GetAccountId());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(cryptohome_id2_),
users[2]->username_hash());
auto* session_manager = session_manager::SessionManager::Get();
EXPECT_EQ(session_manager::SessionState::ACTIVE,
session_manager->session_state());
EXPECT_EQ(3u, session_manager->sessions().size());
EXPECT_EQ(session_manager->sessions()[0].user_account_id, account_id1_);
EXPECT_EQ(session_manager->sessions()[1].user_account_id, account_id2_);
EXPECT_EQ(session_manager->sessions()[2].user_account_id, account_id3_);
}
// Tests crash restore flow for child user.
class CrashRestoreChildUserTest : public MixinBasedInProcessBrowserTest {
protected:
CrashRestoreChildUserTest() {
if (!content::IsPreTest())
login_manager_.set_skip_flags_setup(true);
}
~CrashRestoreChildUserTest() override = default;
// MixinBasedInProcessBrowserTest:
void SetUpCommandLine(base::CommandLine* command_line) override {
if (!content::IsPreTest()) {
const cryptohome::AccountIdentifier cryptohome_id =
cryptohome::CreateAccountIdentifierFromAccountId(
test_user_.account_id);
command_line->AppendSwitchASCII(switches::kLoginUser,
cryptohome_id.account_id());
command_line->AppendSwitchASCII(
switches::kLoginProfile,
CryptohomeClient::GetStubSanitizedUsername(cryptohome_id));
}
MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
// SessionManagerClient has to be in-memory to support setting sessionless
// user policy blob.
chromeos::SessionManagerClient::InitializeFakeInMemory();
MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
MixinBasedInProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts);
base::FilePath user_keys_dir;
ASSERT_TRUE(base::PathService::Get(
chromeos::dbus_paths::DIR_USER_POLICY_KEYS, &user_keys_dir));
std::string sanitized_username =
chromeos::CryptohomeClient::GetStubSanitizedUsername(
cryptohome::CreateAccountIdentifierFromAccountId(
test_user_.account_id));
base::FilePath user_key_file =
user_keys_dir.AppendASCII(sanitized_username).AppendASCII("policy.pub");
const std::string user_key_bits =
user_policy_.GetPublicSigningKeyAsString();
ASSERT_FALSE(user_key_bits.empty());
ASSERT_TRUE(base::CreateDirectory(user_key_file.DirName()));
ASSERT_EQ(base::checked_cast<int>(user_key_bits.length()),
base::WriteFile(user_key_file, user_key_bits.data(),
user_key_bits.length()));
user_policy_.policy_data().set_username(
test_user_.account_id.GetUserEmail());
user_policy_.policy_data().set_gaia_id(test_user_.account_id.GetGaiaId());
user_policy_.Build();
FakeSessionManagerClient::Get()->set_user_policy(
cryptohome::CreateAccountIdentifierFromAccountId(test_user_.account_id),
user_policy_.GetBlob());
}
const LoginManagerMixin::TestUserInfo test_user_{
AccountId::FromUserEmailGaiaId("user@test.com", "123456789"),
user_manager::USER_TYPE_CHILD};
LoginManagerMixin login_manager_{&mixin_host_, {test_user_}};
policy::UserPolicyBuilder user_policy_;
};
IN_PROC_BROWSER_TEST_F(CrashRestoreChildUserTest, PRE_SessionRestore) {
// Verify that child user can log in.
login_manager_.LoginAndWaitForActiveSession(
LoginManagerMixin::CreateDefaultUserContext(test_user_));
}
IN_PROC_BROWSER_TEST_F(CrashRestoreChildUserTest, SessionRestore) {
// Verify that there is no crash on chrome restart.
}
} // namespace chromeos