blob: 05987861284be8cb2113da41f7f883fe7ae31835 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/user_manager/test_helper.h"
#include <cstddef>
#include <optional>
#include "base/check_deref.h"
#include "base/notreached.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
#include "components/account_id/account_id.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_manager_pref_names.h"
#include "components/user_manager/user_names.h"
namespace user_manager {
namespace {
void RegisterPersistedUserInternal(PrefService& local_state,
const AccountId& account_id,
UserType user_type) {
{
ScopedListPrefUpdate update(&local_state, prefs::kRegularUsersPref);
update->Append(account_id.GetUserEmail());
}
{
ScopedDictPrefUpdate update(&local_state, prefs::kUserType);
update->Set(account_id.GetAccountIdKey(), static_cast<int>(user_type));
}
{
KnownUser known_user(&local_state);
known_user.UpdateId(account_id);
}
}
bool IsUserIdMatchingKioskType(std::string_view user_id,
const UserType& expected_kiosk_type) {
auto type_by_user_id = policy::GetDeviceLocalAccountType(user_id);
if (!type_by_user_id.has_value()) {
return false;
}
switch (expected_kiosk_type) {
case UserType::kRegular:
case UserType::kChild:
case UserType::kGuest:
case UserType::kPublicAccount:
NOTREACHED() << "Provided UserType is not kiosk: " << expected_kiosk_type;
case UserType::kKioskChromeApp:
return policy::DeviceLocalAccountType::kKioskApp == type_by_user_id;
case UserType::kKioskWebApp:
return policy::DeviceLocalAccountType::kWebKioskApp == type_by_user_id;
case UserType::kKioskIWA:
return policy::DeviceLocalAccountType::kKioskIsolatedWebApp ==
type_by_user_id;
case UserType::kKioskArcvmApp:
return policy::DeviceLocalAccountType::kArcvmKioskApp == type_by_user_id;
}
}
} // namespace
// static
void TestHelper::RegisterPersistedUser(PrefService& local_state,
const AccountId& account_id) {
RegisterPersistedUserInternal(local_state, account_id, UserType::kRegular);
}
// static
void TestHelper::RegisterPersistedChildUser(PrefService& local_state,
const AccountId& account_id) {
RegisterPersistedUserInternal(local_state, account_id, UserType::kChild);
}
// static
void TestHelper::RegisterKioskAppUser(PrefService& local_state,
std::string_view user_id) {
auto type = policy::GetDeviceLocalAccountType(user_id);
CHECK_EQ(type, policy::DeviceLocalAccountType::kKioskApp)
<< user_id << " did not satisfy to be used for a kiosk user. "
<< "See policy::GetDeviceLocalAccountType for details";
ScopedListPrefUpdate update(&local_state,
prefs::kDeviceLocalAccountsWithSavedData);
update->Append(user_id);
}
// static
void TestHelper::RegisterWebKioskAppUser(PrefService& local_state,
std::string_view user_id) {
auto type = policy::GetDeviceLocalAccountType(user_id);
CHECK_EQ(type, policy::DeviceLocalAccountType::kWebKioskApp)
<< user_id << " did not satisfy to be used for a web kiosk user. "
<< "See policy::GetDeviceLocalAccountType for details";
ScopedListPrefUpdate update(&local_state,
prefs::kDeviceLocalAccountsWithSavedData);
update->Append(user_id);
}
// static
void TestHelper::RegisterPublicAccountUser(PrefService& local_state,
std::string_view user_id) {
auto type = policy::GetDeviceLocalAccountType(user_id);
CHECK_EQ(type, policy::DeviceLocalAccountType::kPublicSession)
<< user_id << " did not satisfy to be used for a public account user. "
<< "See policy::GetDeviceLocalAccountType for details";
ScopedListPrefUpdate update(&local_state,
prefs::kDeviceLocalAccountsWithSavedData);
update->Append(user_id);
}
// static
std::string TestHelper::GetFakeUsernameHash(const AccountId& account_id) {
CHECK(account_id.is_valid());
return ash::UserDataAuthClient::GetStubSanitizedUsername(
cryptohome::CreateAccountIdentifierFromAccountId(account_id));
}
TestHelper::TestHelper(UserManager* user_manager)
: user_manager_(CHECK_DEREF(user_manager)) {}
TestHelper::~TestHelper() = default;
User* TestHelper::AddRegularUser(const AccountId& account_id) {
return AddUserInternal(account_id, UserType::kRegular);
}
User* TestHelper::AddChildUser(const AccountId& account_id) {
return AddUserInternal(account_id, UserType::kChild);
}
User* TestHelper::AddGuestUser() {
return AddUserInternal(GuestAccountId(), UserType::kGuest);
}
User* TestHelper::AddUserInternal(const AccountId& account_id,
UserType user_type) {
// In production, only when there's no logged in users, a user
// can be added to UserManager. Tests should follow the production
// manner for setting up testing environment.
if (!user_manager_->GetLoggedInUsers().empty()) {
LOG(ERROR) << "There already is logged in user(s).";
return nullptr;
}
if (user_manager_->FindUser(account_id)) {
LOG(ERROR) << "User for " << account_id << " already exists";
return nullptr;
}
if (!user_manager_->EnsureUser(account_id, user_type,
/*is_ephemeral=*/false)) {
LOG(ERROR) << "Failed to create a user " << user_type << " for "
<< account_id;
return nullptr;
}
return user_manager_->FindUserAndModify(account_id);
}
User* TestHelper::AddKioskChromeAppUser(std::string_view user_id) {
return AddKioskUser(user_id, UserType::kKioskChromeApp);
}
User* TestHelper::AddKioskWebAppUser(std::string_view user_id) {
return AddKioskUser(user_id, UserType::kKioskWebApp);
}
User* TestHelper::AddKioskIwaUser(std::string_view user_id) {
return AddKioskUser(user_id, UserType::kKioskIWA);
}
User* TestHelper::AddPublicAccountUser(std::string_view user_id) {
// Quick check that the `user_id` satisfies kiosk-app type.
auto type = policy::GetDeviceLocalAccountType(user_id);
if (type != policy::DeviceLocalAccountType::kPublicSession) {
LOG(ERROR)
<< "user_id (" << user_id << ") did not satisfy to be used for "
<< "a public account user. See policy::GetDeviceLocalAccountType "
<< "for details.";
return nullptr;
}
return AddDeviceLocalAccountUserInternal(user_id, UserType::kPublicAccount);
}
User* TestHelper::AddDeviceLocalAccountUserInternal(std::string_view user_id,
UserType user_type) {
// Build DeviceLocalAccountInfo for the existing users.
std::vector<UserManager::DeviceLocalAccountInfo> device_local_accounts;
for (const auto& user : user_manager_->GetPersistedUsers()) {
if (user->GetAccountId().GetUserEmail() == user_id) {
LOG(ERROR) << "duplicated account is found: " << user_id;
return nullptr;
}
if (!user->IsDeviceLocalAccount()) {
continue;
}
UserManager::DeviceLocalAccountInfo info(
user->GetAccountId().GetUserEmail(), user->GetType());
if (user->GetType() == UserType::kPublicAccount) {
info.display_name = user->GetDisplayName();
}
device_local_accounts.push_back(info);
}
// Add the given `user_id`.
device_local_accounts.emplace_back(std::string(user_id), user_type);
user_manager_->UpdateDeviceLocalAccountUser(device_local_accounts);
return user_manager_->FindUserAndModify(AccountId::FromUserEmail(user_id));
}
User* TestHelper::AddKioskUser(std::string_view user_id, UserType kiosk_type) {
if (!IsUserIdMatchingKioskType(user_id, kiosk_type)) {
LOG(ERROR) << "user_id (" << user_id << ") did not satisfy to be used for "
<< "a kiosk with specified type: " << kiosk_type
<< ". See policy::GetDeviceLocalAccountType for details.";
return nullptr;
}
return AddDeviceLocalAccountUserInternal(user_id, kiosk_type);
}
} // namespace user_manager