blob: 095a815a01ea920e00e36376c5d457a306be0354 [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_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/session/user_session_manager.h"
#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.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/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/dbus/session_manager/session_manager_client.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_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_);
}
} // namespace chromeos