0

Move user creation from session creation.

Currently, if missing and some condition meets, a User instance is
created in UserManager::UserLoggedIn called from
SessionManager::CreateSession. This approach is difficult to write
proper test, because the expecting condition is too complex.

This CL moves that part to the callers of CreateSession family.
Now, UserLoggedIn handles log-in. Callers now have responsibility
to register User before creating the session.

In the production, no behavior change is expected.
In the tests, the behavior of UserManager is now much aligned
with fake implementation.

BUG=278643115
TEST=Tryjob

Change-Id: Ia746f845a6967288ff5626f5507b9da71c5d8487
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6239080
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1417065}
This commit is contained in:
Hidehiko Abe
2025-02-06 15:38:34 -08:00
committed by Chromium LUCI CQ
parent c5f8015ca0
commit d5f6403f66
32 changed files with 412 additions and 185 deletions

@ -51,6 +51,8 @@
#include "components/prefs/scoped_user_pref_update.h"
#include "components/session_manager/core/session_manager.h"
#include "components/supervised_user/core/common/supervised_user_constants.h"
#include "components/user_manager/test_helper.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
@ -1817,10 +1819,16 @@ class AccessibilityManagerLoginTest : public OobeBaseTest {
}
void CreateSession(const AccountId& account_id) {
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(account_id));
auto* session_manager = session_manager::SessionManager::Get();
session_manager->CreateSession(account_id, account_id.GetUserEmail(),
user_manager::UserType::kRegular,
/*has_active_session=*/false);
session_manager->CreateSession(
account_id,
// TODO(crbug.com/278643115): Use fake username hash.
account_id.GetUserEmail(),
/*new_user=*/false,
/*has_active_session=*/false);
}
void StartUserSession(const AccountId& account_id) {

@ -24,6 +24,9 @@
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/test_helper.h"
#include "components/user_manager/user_manager.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/browser_test.h"
#include "google_apis/gaia/gaia_id.h"
@ -81,13 +84,26 @@ bool GetScreenMagnifierEnabledFromPref() {
return prefs()->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled);
}
[[nodiscard]] bool AddRegularUser(const AccountId& account_id) {
return user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(account_id);
}
void LogIn(const AccountId& account_id) {
session_manager::SessionManager::Get()->CreateSession(
account_id,
user_manager::FakeUserManager::GetFakeUsernameHash(account_id),
/*new_user=*/false,
/*has_active_session=*/false);
}
// Creates and logs into a profile with account |account_id|, and makes sure
// that the profile is regarded as "non new" in the next login. This is used in
// PRE_XXX cases so that in the main XXX case we can test non new profiles.
void PrepareNonNewProfile(const AccountId& account_id) {
session_manager::SessionManager::Get()->CreateSession(
account_id, account_id.GetUserEmail(), user_manager::UserType::kRegular,
/*has_active_session=*/false);
CHECK(AddRegularUser(account_id));
LogIn(account_id);
// To prepare a non-new profile for tests, we must ensure the profile
// directory and the preference files are created, because that's what
// Profile::IsNewProfile() checks. CreateSession(), however, does not yet
@ -166,8 +182,6 @@ class MagnificationManagerTest : public InProcessBrowserTest {
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kLoginManager);
command_line->AppendSwitchASCII(switches::kLoginProfile,
TestingProfile::kTestUserProfileDir);
command_line->AppendSwitch(switches::kAllowFailedPolicyFetchForTest);
}
@ -198,9 +212,7 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginOffToOff) {
EXPECT_FALSE(IsMagnifierEnabled());
// Logs in with existing profile.
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
LogIn(test_account_id_);
// Confirms that magnifier is still disabled just after login.
EXPECT_FALSE(IsMagnifierEnabled());
@ -236,9 +248,7 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginFullToOff) {
EXPECT_EQ(2.5, GetFullScreenMagnifierScale());
// Logs in (but the session is not started yet).
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
LogIn(test_account_id_);
// Confirms that magnifier is keeping enabled.
EXPECT_TRUE(IsMagnifierEnabled());
@ -265,9 +275,7 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginOffToFull) {
EXPECT_FALSE(IsMagnifierEnabled());
// Logs in (but the session is not started yet).
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
LogIn(test_account_id_);
// Confirms that magnifier is keeping disabled.
EXPECT_FALSE(IsMagnifierEnabled());
@ -298,9 +306,7 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginFullToFull) {
EXPECT_EQ(3.0, GetFullScreenMagnifierScale());
// Logs in (but the session is not started yet).
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
LogIn(test_account_id_);
// Confirms that magnifier is keeping enabled.
EXPECT_TRUE(IsMagnifierEnabled());
@ -325,9 +331,7 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginFullToUnset) {
EXPECT_TRUE(IsMagnifierEnabled());
// Logs in (but the session is not started yet).
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
LogIn(test_account_id_);
// Confirms that magnifier is keeping enabled.
EXPECT_TRUE(IsMagnifierEnabled());
@ -344,9 +348,8 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginAsNewUserUnset) {
EXPECT_FALSE(IsMagnifierEnabled());
// Logs in (but the session is not started yet).
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
ASSERT_TRUE(AddRegularUser(test_account_id_));
LogIn(test_account_id_);
// Confirms that magnifier is keeping disabled.
EXPECT_FALSE(IsMagnifierEnabled());
@ -360,9 +363,8 @@ IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, LoginAsNewUserUnset) {
IN_PROC_BROWSER_TEST_F(MagnificationManagerTest, TypePref) {
// Logs in
session_manager::SessionManager::Get()->CreateSession(
test_account_id_, kTestUserName, user_manager::UserType::kRegular,
/*has_active_session=*/false);
ASSERT_TRUE(AddRegularUser(test_account_id_));
LogIn(test_account_id_);
StartUserSession(test_account_id_);
// Confirms that magnifier is disabled just after login.

@ -107,12 +107,14 @@ class EventBasedStatusReportingServiceTest : public testing::Test {
profile_ = std::make_unique<TestingProfile>();
profile_->SetIsSupervisedProfile();
// TODO(hidehiko): we should set up kChild account from the beginning,
// but ArcAppTest does not support such a case. Fix the test helper.
arc_test_.SetUp(profile());
session_manager_.CreateSession(
account_id(),
user_manager::FakeUserManager::GetFakeUsernameHash(account_id()),
user_manager::UserType::kChild,
/*new_user=*/false,
/*has_active_session=*/false);
session_manager_.SetSessionState(
session_manager::SessionState::LOGIN_PRIMARY);

@ -486,7 +486,7 @@ class MultiProfileDriveFileSystemExtensionApiTest
session_manager::SessionManager::Get()->CreateSession(
AccountId::FromUserEmailGaiaId(kSecondProfileAccount,
kSecondProfileGaiaId),
kSecondProfileHash, user_manager::UserType::kRegular,
kSecondProfileHash, /*new_user=*/false,
/*has_active_session=*/false);
base::FilePath profile_dir = user_data_directory.AppendASCII(
ash::BrowserContextHelper::GetUserBrowserContextDirName(

@ -125,7 +125,7 @@ class ScreenLockerUnitTest : public testing::Test {
auto* session_manager = session_manager::SessionManager::Get();
session_manager->CreateSession(user->GetAccountId(),
user->GetAccountId().GetUserEmail(),
user->GetType(),
/*new_user=*/false,
/*has_active_session=*/false);
auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
ASSERT_TRUE(primary_user);

@ -20,6 +20,7 @@
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/base_paths.h"
#include "base/check_deref.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
@ -1179,9 +1180,47 @@ void UserSessionManager::CreateUserSession(const UserContext& user_context,
has_auth_cookies_ = has_auth_cookies;
ProcessAppModeSwitches();
StoreUserContextDataBeforeProfileIsCreated();
session_manager::SessionManager::Get()->CreateSession(
user_context_.GetAccountId(), user_context_.GetUserIDHash(),
user_context.GetUserType(), has_active_session);
// Ensure there's user to be logged in.
// TODO(hidehiko): probably we should have better place to guarantee User
// registered much before this stage. Investigate further.
const AccountId& account_id = user_context_.GetAccountId();
auto& user_manager = CHECK_DEREF(user_manager::UserManager::Get());
auto& session_manager = CHECK_DEREF(session_manager::SessionManager::Get());
// Find persisted user.
user_manager::User* persisted_user = nullptr;
for (auto& user : user_manager.GetPersistedUsers()) {
if (user->GetAccountId() == account_id) {
persisted_user = user.get();
break;
}
}
bool created = false;
if (session_manager.sessions().empty()) {
// Primary session login.
user_manager::UserType user_type = user_manager.CalculateUserType(
account_id, persisted_user,
/*browser_restart=*/false,
user_context_.GetUserType() == user_manager::UserType::kChild);
// TODO(crbug.com/278643115): Make sure user_type and
// user_context_.GetUserType() are the same value, and then remove
// CalculateUserType call.
// Note: we call EnsureUser even if there's persisted_user,
// which may update user type between kRegular and kChild.
created = user_manager.EnsureUser(
account_id, user_type, user_manager.IsEphemeralAccountId(account_id));
} else {
CHECK(persisted_user);
// TODO(crbug.com/278643115): Make sure persisted_user's type and
// user_context_.GetUserType() are the same value.
}
session_manager.CreateSession(user_context_.GetAccountId(),
user_context_.GetUserIDHash(), created,
has_active_session);
}
void UserSessionManager::PreStartSession(StartSessionType start_session_type) {

@ -269,18 +269,15 @@ void FakeChromeUserManager::UserLoggedIn(const AccountId& account_id,
}
}
if (!active_user_ && IsEphemeralAccountId(account_id)) {
// TODO(crbug.com/278643115): Temporarily duplicate the logic
// of ephemeral user creation. This class should be migrated into
// FakeUserManager.
active_user_ =
AddEphemeralUser(account_id, user_manager::UserType::kRegular);
SetIsCurrentUserNew(true);
}
NotifyOnLogin();
}
bool FakeChromeUserManager::EnsureUser(const AccountId& account_id,
user_manager::UserType user_type,
bool is_ephemeral) {
NOTREACHED();
}
void FakeChromeUserManager::SwitchToLastActiveUser() {
NOTREACHED();
}

@ -77,6 +77,9 @@ class FakeChromeUserManager : public user_manager::UserManagerImpl {
const std::string& user_id_hash,
bool browser_restart,
bool is_child) override;
bool EnsureUser(const AccountId& account_id,
user_manager::UserType user_type,
bool is_ephemeral) override;
void SwitchActiveUser(const AccountId& account_id) override;
void SwitchToLastActiveUser() override;
void OnSessionStarted() override;

@ -488,6 +488,8 @@ TEST_F(UserManagerTest, DoNotSaveKioskAccountsToKRegularUsersPref) {
kKioskAccountId, kKioskAccountId.GetUserEmail(),
false /* browser_restart */, false /* is_child */);
ResetUserManager();
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kAccountId0));
user_manager::UserManager::Get()->UserLoggedIn(
kAccountId0, kAccountId0.GetUserEmail(), false /* browser_restart */,
false /* is_child */);
@ -511,6 +513,8 @@ TEST_F(UserManagerTest, DoNotSaveKioskAccountsToKRegularUsersPref) {
TEST_F(UserManagerTest, RemoveUser) {
// Create owner account and login in.
ASSERT_TRUE(
user_manager::TestHelper(*user_manager_).AddRegularUser(kOwnerAccountId));
user_manager_->UserLoggedIn(kOwnerAccountId, kOwnerAccountId.GetUserEmail(),
false /* browser_restart */,
false /* is_child */);
@ -519,6 +523,8 @@ TEST_F(UserManagerTest, RemoveUser) {
ResetUserManager();
// Create non-owner account and login in.
ASSERT_TRUE(
user_manager::TestHelper(*user_manager_).AddRegularUser(kAccountId0));
user_manager_->UserLoggedIn(kAccountId0, kAccountId0.GetUserEmail(),
false /* browser_restart */,
false /* is_child */);
@ -574,14 +580,20 @@ TEST_F(UserManagerTest, RemoveUser) {
}
TEST_F(UserManagerTest, RemoveRegularUsersExceptOwnerFromList) {
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kOwnerAccountId));
user_manager::UserManager::Get()->UserLoggedIn(
kOwnerAccountId, kOwnerAccountId.GetUserEmail(),
false /* browser_restart */, false /* is_child */);
ResetUserManager();
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kAccountId0));
user_manager::UserManager::Get()->UserLoggedIn(
kAccountId0, kAccountId0.GetUserEmail(), false /* browser_restart */,
false /* is_child */);
ResetUserManager();
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kAccountId1));
user_manager::UserManager::Get()->UserLoggedIn(
kAccountId1, kAccountId1.GetUserEmail(), false /* browser_restart */,
false /* is_child */);
@ -619,10 +631,14 @@ TEST_F(UserManagerTest, RegularUserLoggedInAsEphemeral) {
/* owner= */ kOwnerAccountId.GetUserEmail());
RetrieveTrustedDevicePolicies();
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kOwnerAccountId));
user_manager::UserManager::Get()->UserLoggedIn(
kOwnerAccountId, kOwnerAccountId.GetUserEmail(),
false /* browser_restart */, false /* is_child */);
ResetUserManager();
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kAccountId0));
user_manager::UserManager::Get()->UserLoggedIn(
kAccountId0, kAccountId0.GetUserEmail(), false /* browser_restart */,
false /* is_child */);
@ -636,6 +652,8 @@ TEST_F(UserManagerTest, RegularUserLoggedInAsEphemeral) {
TEST_F(UserManagerTest, ScreenLockAvailability) {
// Log in the user and create the profile.
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kOwnerAccountId));
user_manager::UserManager::Get()->UserLoggedIn(
kOwnerAccountId, kOwnerAccountId.GetUserEmail(),
false /* browser_restart */, false /* is_child */);
@ -663,6 +681,8 @@ TEST_F(UserManagerTest, ScreenLockAvailability) {
}
TEST_F(UserManagerTest, ProfileRequiresPolicyUnknown) {
ASSERT_TRUE(user_manager::TestHelper(*user_manager::UserManager::Get())
.AddRegularUser(kOwnerAccountId));
user_manager::UserManager::Get()->UserLoggedIn(
kOwnerAccountId, kOwnerAccountId.GetUserEmail(), false, false);
user_manager::KnownUser known_user(local_state_->Get());
@ -731,6 +751,8 @@ TEST_F(UserManagerTest,
// callback.
TEST_F(UserManagerTest, ProfilePrefs) {
// Simulates login.
ASSERT_TRUE(
user_manager::TestHelper(*user_manager_).AddRegularUser(kAccountId0));
user_manager_->UserLoggedIn(kAccountId0, kAccountId0.GetUserEmail(),
/*browser_restart=*/false,
/*is_child=*/false);
@ -756,4 +778,96 @@ TEST_F(UserManagerTest, ProfilePrefs) {
user_manager_->OnUserProfileWillBeDestroyed(kAccountId0);
}
TEST_F(UserManagerTest, EnsureUserRegular) {
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
EXPECT_FALSE(user_manager_->FindUser(kAccountId0));
EXPECT_TRUE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kRegular,
/*is_ephemeral=*/false));
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 1u);
EXPECT_TRUE(user_manager_->FindUser(kAccountId0));
// Calling EnsureUser for the existing user is no-op.
EXPECT_FALSE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kRegular,
/*is_ephemeral=*/false));
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 1u);
}
TEST_F(UserManagerTest, EnsureUserEphemeralRegular) {
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
EXPECT_FALSE(user_manager_->FindUser(kAccountId0));
EXPECT_TRUE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kRegular,
/*is_ephemeral=*/true));
// Ephemeral user should not be listed in persisted list.
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
EXPECT_TRUE(user_manager_->FindUser(kAccountId0));
}
TEST_F(UserManagerTest, EnsureUserChild) {
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
EXPECT_FALSE(user_manager_->FindUser(kAccountId0));
EXPECT_TRUE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kChild,
/*is_ephemeral=*/false));
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 1u);
EXPECT_TRUE(user_manager_->FindUser(kAccountId0));
// Calling EnsureUser for the existing user is no-op.
EXPECT_FALSE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kChild,
/*is_ephemeral=*/false));
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 1u);
}
TEST_F(UserManagerTest, EnsureUserTypeSwitch) {
// EnsureUser may switch UserType between kRegular and kChild.
ASSERT_TRUE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kRegular,
/*is_ephemeral=*/false));
auto* user = user_manager_->FindUser(kAccountId0);
ASSERT_TRUE(user);
EXPECT_EQ(user->GetType(), user_manager::UserType::kRegular);
// Switch from kRegular to kChild.
EXPECT_FALSE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kChild,
/*is_ephemeral=*/false));
EXPECT_EQ(user->GetType(), user_manager::UserType::kChild);
// Move back from kChild to kRegular.
EXPECT_FALSE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kRegular,
/*is_ephemeral=*/false));
EXPECT_EQ(user->GetType(), user_manager::UserType::kRegular);
}
TEST_F(UserManagerTest, EnsureUserGuest) {
EXPECT_FALSE(user_manager_->FindUser(user_manager::GuestAccountId()));
EXPECT_TRUE(user_manager_->EnsureUser(user_manager::GuestAccountId(),
user_manager::UserType::kGuest,
/*is_ephemeral=*/false));
auto* user = user_manager_->FindUser(user_manager::GuestAccountId());
ASSERT_TRUE(user);
EXPECT_EQ(user->GetType(), user_manager::UserType::kGuest);
// Guest user is not in a persisted list.
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
}
TEST_F(UserManagerTest, EnsureUserPublicAccount) {
EXPECT_FALSE(user_manager_->FindUser(kAccountId0));
EXPECT_TRUE(user_manager_->EnsureUser(kAccountId0,
user_manager::UserType::kPublicAccount,
/*is_ephemeral=*/false));
EXPECT_TRUE(user_manager_->FindUser(kAccountId0));
// Creation of a PublicAccount User happens only when, in the previous
// chrome process, it's marked as deleted, then Chrome is restarted (e.g.
// due to crash). In the case, the created user should not be listed in
// the persisted list.
EXPECT_EQ(user_manager_->GetPersistedUsers().size(), 0u);
}
} // namespace ash

@ -1092,13 +1092,27 @@ void ChromeBrowserMainPartsAsh::PreProfileInit() {
return;
}
// In case of multi-profiles --login-profile will contain user_id_hash.
std::string user_id_hash =
auto& session_manager = CHECK_DEREF(session_manager::SessionManager::Get());
CHECK(session_manager.sessions().empty());
bool created = false;
if (!user_manager->FindUser(account_id)) {
// TODO(crbug.com/278643115): Because we know here's browser_restart,
// the user_type calculation can be much simplified.
user_manager::UserType user_type = user_manager->CalculateUserType(
account_id, /*user=*/nullptr, /*browser_restart=*/true,
/*is_child=*/false);
// If there's no User found, that means, in the previous Chrome process,
// the user was ephemeral (including Guest).
created = user_manager->EnsureUser(account_id, user_type,
/*is_ephemeral=*/true);
}
// In case of multi-profiles --login-profile will contain username_hash.
std::string username_hash =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kLoginProfile);
session_manager::SessionManager::Get()->CreateSessionForRestart(
account_id, user_id_hash);
session_manager.CreateSessionForRestart(account_id, username_hash, created);
// If restarting demo session, mark demo session as started before primary
// profile starts initialization so browser context keyed services created
@ -1107,7 +1121,7 @@ void ChromeBrowserMainPartsAsh::PreProfileInit() {
DemoSession::StartIfInDemoMode();
VLOG(1) << "Relaunching browser for user: " << account_id.Serialize()
<< " with hash: " << user_id_hash;
<< " with hash: " << username_hash;
}
}

@ -569,7 +569,8 @@ void AutomaticRebootManagerBasicTest::LogIn(user_manager::User* user) {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/false,
/*has_active_session=*/false);
session_manager_.SessionStarted();
session_manager_.SetSessionState(session_manager::SessionState::ACTIVE);

@ -1086,7 +1086,8 @@ class MultiProfileDownloadNotificationTest
void AddUser(const TestAccountInfo& info) {
session_manager::SessionManager::Get()->CreateSession(
AccountId::FromUserEmailGaiaId(info.email, GaiaId(info.gaia_id)),
info.hash, user_manager::UserType::kRegular,
info.hash,
/*new_user=*/false,
/*has_active_session=*/false);
user_manager::UserManager::Get()->SaveUserDisplayName(

@ -627,7 +627,8 @@ class CertVerifierMultiProfileUserSettingsTest
session_manager::SessionManager::Get()->CreateSession(
AccountId::FromUserEmailGaiaId(kSecondaryUserAccount,
kSecondaryUserGaiaId),
kSecondaryUserHash, user_manager::UserType::kRegular,
kSecondaryUserHash,
/*new_user=*/false,
/*has_active_session=*/false);
// Set up the secondary profile.
base::FilePath profile_dir = user_data_directory.Append(

@ -88,6 +88,7 @@
#include "chromeos/ash/experiences/arc/session/arc_management_transition.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/test_helper.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_manager_impl.h"
#include "components/user_manager/user_names.h"
@ -295,12 +296,24 @@ class ProfileManagerTest : public testing::Test {
// Helper function to register an user with id |user_id| and create profile
// with a correct path.
void RegisterUser(const AccountId& account_id) {
ash::ProfileHelper* profile_helper = ash::ProfileHelper::Get();
auto* user_manager = user_manager::UserManager::Get();
// Add user for testing.
{
user_manager::TestHelper test_helper(*user_manager);
if (account_id == user_manager::GuestAccountId()) {
ASSERT_TRUE(test_helper.AddGuestUser());
} else {
ASSERT_TRUE(test_helper.AddRegularUser(account_id));
}
}
const std::string user_id_hash =
user_manager::FakeUserManager::GetFakeUsernameHash(account_id);
user_manager::UserManager::Get()->UserLoggedIn(account_id, user_id_hash,
false /* browser_restart */,
false /* is_child */);
ash::ProfileHelper* profile_helper = ash::ProfileHelper::Get();
g_browser_process->profile_manager()->GetProfile(
profile_helper->GetProfilePathByUserIdHash(user_id_hash));
}
@ -323,6 +336,15 @@ class ProfileManagerTest : public testing::Test {
const base::FilePath dest_path =
profile_helper->GetProfilePathByUserIdHash(user_id_hash);
{
user_manager::TestHelper test_helper(*user_manager);
if (user_is_child) {
CHECK(test_helper.AddChildUser(account_id));
} else {
CHECK(test_helper.AddRegularUser(account_id));
}
}
TestingProfile::Builder builder;
builder.SetPath(dest_path);
builder.SetIsNewProfile(profile_is_new);
@ -452,9 +474,11 @@ TEST_F(ProfileManagerTest, UserProfileLoading) {
"test-user@example.com", GaiaId("0123456789"));
const std::string user_id_hash =
user_manager::FakeUserManager::GetFakeUsernameHash(account_id);
user_manager::UserManager::Get()->UserLoggedIn(account_id, user_id_hash,
false /* browser_restart */,
false /* is_child */);
auto* user_manager = user_manager::UserManager::Get();
ASSERT_TRUE(
user_manager::TestHelper(*user_manager).AddRegularUser(account_id));
user_manager->UserLoggedIn(account_id, user_id_hash,
false /* browser_restart */, false /* is_child */);
// Sign-in profile should be returned at this stage. Otherwise, login code
// ends up in an invalid state. Strange things as in http://crbug.com/728683
@ -956,13 +980,6 @@ class ProfileManagerGuestTest : public ProfileManagerTest {
return profile_manager_unique;
}
#if BUILDFLAG(IS_CHROMEOS)
ash::FakeChromeUserManager* GetFakeUserManager() const {
return static_cast<ash::FakeChromeUserManager*>(
user_manager::UserManager::Get());
}
#endif
private:
raw_ptr<UnittestGuestProfileManager, DanglingUntriaged>
unittest_profile_manager_ = nullptr;

@ -5734,7 +5734,7 @@ class SSLUITestCustomCACerts : public SSLUITestNoCert {
session_manager::SessionManager::Get()->CreateSession(
AccountId::FromUserEmailGaiaId(kSecondaryUserAccount,
kSecondaryUserGaiaId),
kSecondaryUserHash, user_manager::UserType::kRegular,
kSecondaryUserHash, /*new_user=*/false,
/*has_active_session=*/false);
// Set up the secondary profile.
base::FilePath profile_dir = user_data_directory.Append(

@ -69,7 +69,7 @@ class AssistantBrowserDelegateImplTest : public ChromeAshTestBase {
session_manager->CreateSession(
account_id,
user_manager::FakeUserManager::GetFakeUsernameHash(account_id),
user->GetType(),
/*new_user=*/false,
/*has_active_session=*/false);
session_manager->SessionStarted();

@ -46,7 +46,7 @@ void CreateAndStartUserSession(const AccountId& account_id) {
const std::string user_id_hash =
user_manager::FakeUserManager::GetFakeUsernameHash(account_id);
SessionManager::Get()->CreateSession(account_id, user_id_hash,
user_manager::UserType::kRegular,
/*new_user=*/false,
/*has_active_session=*/false);
profiles::testing::CreateProfileSync(
g_browser_process->profile_manager(),

@ -123,7 +123,8 @@ class SessionControllerClientImplTest : public testing::Test {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/false,
/*has_active_session=*/false);
// Simulate that user profile is loaded.
@ -428,7 +429,8 @@ TEST_F(SessionControllerClientImplTest, SendUserSession) {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/false,
/*has_active_session=*/false);
session_manager().SetSessionState(SessionState::ACTIVE);
@ -485,7 +487,8 @@ TEST_F(SessionControllerClientImplTest, UserPrefsChange) {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/false,
/*has_active_session=*/false);
// Simulate the notification that the profile is ready.

@ -931,7 +931,8 @@ class RelaunchNotificationControllerPlatformImplTest : public ::testing::Test {
// SessionManager is created by
// |AshTestHelper::bluetooth_config_test_helper()|.
session_manager::SessionManager::Get()->CreateSession(
user->GetAccountId(), test_user_email, user->GetType(),
user->GetAccountId(), test_user_email,
/*new_user=*/false,
/*has_active_session=*/false);
session_manager::SessionManager::Get()->SetSessionState(
session_manager::SessionState::ACTIVE);

@ -140,7 +140,7 @@ class WebAppProfileDeletionBrowserTest : public WebAppBrowserTestBase {
Profile& StartUserSession(const AccountId& account_id) {
auto* session_manager = session_manager::SessionManager::Get();
session_manager->CreateSession(account_id, account_id.GetUserEmail(),
user_manager::UserType::kRegular,
/*new_user=*/false,
/*has_active_session=*/false);
ProfileManager* profile_manager = g_browser_process->profile_manager();

@ -347,13 +347,9 @@ TEST_F(ArcUtilTest, IsArcAllowedForUser) {
AccountId::FromUserEmailGaiaId("user5@test.com", GaiaId("1234567890-5")),
user_manager::UserType::kChild)));
// An ephemeral user is a logged in user but unknown to UserManager when
// ephemeral policy is set.
fake_user_manager->SetEphemeralModeConfig(
user_manager::UserManager::EphemeralModeConfig(
/* included_by_default= */ true,
/* include_list= */ std::vector<AccountId>{},
/* exclude_list= */ std::vector<AccountId>{}));
// Set up public account user.
fake_user_manager->AddPublicAccountUser(
AccountId::FromUserEmailGaiaId("test@test.com", GaiaId("9876543210")));
fake_user_manager->UserLoggedIn(
AccountId::FromUserEmailGaiaId("test@test.com", GaiaId("9876543210")),
"test@test.com-hash", false /* browser_restart */, false /* is_child */);
@ -361,6 +357,8 @@ TEST_F(ArcUtilTest, IsArcAllowedForUser) {
ASSERT_TRUE(ephemeral_user);
ASSERT_TRUE(fake_user_manager->IsUserCryptohomeDataEphemeral(
ephemeral_user->GetAccountId()));
EXPECT_TRUE(IsArcAllowedForUser(ephemeral_user));
}
TEST_F(ArcUtilTest, ArcStartModeDefault) {

@ -78,7 +78,6 @@ class BluetoothPowerControllerImplTest : public testing::Test {
AccountId::FromUserEmailGaiaId(display_email, gaia_id),
user_manager::UserType::kRegular);
}
fake_user_manager_->SetIsCurrentUserNew(is_new_profile);
// Create a session in SessionManager. This will also login the user in
// UserManager.
@ -87,7 +86,8 @@ class BluetoothPowerControllerImplTest : public testing::Test {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/is_new_profile,
/*has_active_session=*/false);
session_manager_->SessionStarted();

@ -123,7 +123,8 @@ class SystemPropertiesProviderImplTest : public testing::Test {
// TODO(crbug.com/278643115): Looks incorrect.
// User's username_hash should be set inside CreateSession via
// UserManager::UserLoggedIn().
user->username_hash(), user->GetType(),
user->username_hash(),
/*new_user=*/false,
/*has_active_session=*/false);
session_manager_->SessionStarted();

@ -5,6 +5,7 @@
#include "components/session_manager/core/session_manager.h"
#include "base/check.h"
#include "base/check_deref.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
@ -45,8 +46,8 @@ void SessionManager::SetSessionState(SessionState state) {
}
void SessionManager::CreateSession(const AccountId& user_account_id,
const std::string& user_id_hash,
user_manager::UserType user_type,
const std::string& username_hash,
bool new_user,
bool has_active_session) {
// For secondary user log-in in common cases, we switch the active
// session after user Profile is created, so data needed for UI update,
@ -57,22 +58,15 @@ void SessionManager::CreateSession(const AccountId& user_account_id,
if (!has_active_session && !sessions_.empty()) {
pending_active_account_id_ = user_account_id;
}
CreateSessionInternal(user_account_id, user_id_hash,
false /* browser_restart */,
// TODO(crbug.com/278643115): Respect given `user_type`
// instead of estimating from surrounding info.
user_type == user_manager::UserType::kChild);
CreateSessionInternal(user_account_id, username_hash, new_user,
/*browser_restart=*/false);
}
void SessionManager::CreateSessionForRestart(const AccountId& user_account_id,
const std::string& user_id_hash) {
CHECK(user_manager_);
const user_manager::User* user = user_manager_->FindUser(user_account_id);
// Tests do not always create users.
const bool is_child =
user && user->GetType() == user_manager::UserType::kChild;
CreateSessionInternal(user_account_id, user_id_hash,
true /* browser_restart */, is_child);
const std::string& username_hash,
bool new_user) {
CreateSessionInternal(user_account_id, username_hash, new_user,
/*browser_restart=*/true);
}
void SessionManager::OnUserManagerCreated(
@ -174,16 +168,27 @@ void SessionManager::SetInstance(SessionManager* session_manager) {
}
void SessionManager::CreateSessionInternal(const AccountId& user_account_id,
const std::string& user_id_hash,
bool browser_restart,
bool is_child) {
const std::string& username_hash,
bool new_user,
bool browser_restart) {
CHECK(user_manager_);
DCHECK(!HasSessionForAccountId(user_account_id));
const auto& user = CHECK_DEREF(user_manager_->FindUser(user_account_id));
// TODO(crbug.com/278643115): This attribute looks like the one for Session
// rather than UserManager. Move the field.
// Note: For KioskApp user, this may be updated later in UserSessionManager.
user_manager_->SetIsCurrentUserNew(
(new_user && user.HasGaiaAccount()) ||
user.GetType() == user_manager::UserType::kPublicAccount);
observers_.Notify(&SessionManagerObserver::OnSessionCreationStarted,
user_account_id);
sessions_.push_back(std::make_unique<Session>(next_id_++, user_account_id));
user_manager_->UserLoggedIn(user_account_id, user_id_hash, browser_restart,
is_child);
user_manager_->UserLoggedIn(user_account_id, username_hash,
/*browser_restart=*/false, // unused
/*is_child=*/false); // unused
OnSessionCreated(browser_restart);
observers_.Notify(&SessionManagerObserver::OnSessionCreated, user_account_id);
}

@ -42,8 +42,8 @@ class SESSION_EXPORT SessionManager
// for the secondary+ users. For the latter case, `has_active_session`
// is set true.
void CreateSession(const AccountId& user_account_id,
const std::string& user_id_hash,
user_manager::UserType user_type,
const std::string& username_hash,
bool new_user,
bool has_active_session);
// Similar to above, creates a session for the given user and hash,
@ -54,7 +54,8 @@ class SESSION_EXPORT SessionManager
// the user type should be derived from the one. Though, there are edge
// cases. Please find UserManager::CalculateUserType() for details.
void CreateSessionForRestart(const AccountId& user_account_id,
const std::string& user_id_hash);
const std::string& user_id_hash,
bool new_user);
// Returns true if we're logged in and browser has been started i.e.
// browser_creator.LaunchBrowser(...) was called after sign in
@ -124,9 +125,9 @@ class SESSION_EXPORT SessionManager
private:
void CreateSessionInternal(const AccountId& user_account_id,
const std::string& user_id_hash,
bool browser_restart,
bool is_child);
const std::string& username_hash,
bool new_user,
bool browser_restart);
// Pointer to the existing SessionManager instance (if any).
// Set in ctor, reset in dtor. Not owned since specific implementation of

@ -57,17 +57,15 @@ void FakeUserManager::UserLoggedIn(const AccountId& account_id,
}
}
if (!active_user_ && IsEphemeralAccountId(account_id)) {
// TODO(crbug.com/278643115): Temporarily duplicate the logic
// of ephemeral user creation. This method should be unified with
// UserManagerImpl::UserLoggedIn eventually.
active_user_ = AddEphemeralUser(account_id, UserType::kRegular);
SetIsCurrentUserNew(true);
}
NotifyOnLogin();
}
bool FakeUserManager::EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral) {
NOTREACHED();
}
void FakeUserManager::SwitchActiveUser(const AccountId& account_id) {
for (UserList::const_iterator it = logged_in_users_.begin();
it != logged_in_users_.end(); ++it) {

@ -35,6 +35,9 @@ class USER_MANAGER_EXPORT FakeUserManager : public UserManagerImpl {
const std::string& username_hash,
bool browser_restart,
bool is_child) override;
bool EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral) override;
void SwitchActiveUser(const AccountId& account_id) override;
// Just make it public for tests.

@ -10,6 +10,7 @@
#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 {
@ -31,6 +32,34 @@ TestHelper::TestHelper(UserManager& 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) {
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::AddKioskAppUser(std::string_view user_id) {
// Quick check that the `user_id` satisfies kiosk-app type.
auto type = policy::GetDeviceLocalAccountType(user_id);

@ -8,6 +8,7 @@
#include <string_view>
#include "base/memory/raw_ref.h"
#include "components/user_manager/user_type.h"
class AccountId;
class PrefService;
@ -32,11 +33,24 @@ class TestHelper {
explicit TestHelper(UserManager& user_manager);
~TestHelper();
// Creates and adds a new Kiosk user.
// On failure, nullptr is returned.
// Creates and adds a regular (persisted) user, and returns it.
// On failure, returns nullptr.
[[nodiscard]] User* AddRegularUser(const AccountId& account_id);
// Creates and adds a child user, and returns it.
[[nodiscard]] User* AddChildUser(const AccountId& account_id);
// Creates and adds a guest user, and returns it.
// On failure, returns nullptr.
[[nodiscard]] User* AddGuestUser();
// Creates and adds a new Kiosk user, and returns it.
// On failure, returns nullptr.
[[nodiscard]] User* AddKioskAppUser(std::string_view user_id);
private:
User* AddUserInternal(const AccountId& account_id, UserType user_type);
raw_ref<UserManager> user_manager_;
};

@ -249,16 +249,26 @@ class USER_MANAGER_EXPORT UserManager {
// Returns account Id of the user that was active in the previous session.
virtual const AccountId& GetLastSessionActiveAccountId() const = 0;
// Indicates that a user with the given |account_id| has just logged in. The
// persistent list is updated accordingly if the user is not ephemeral.
// |browser_restart| is true when reloading Chrome after crash to distinguish
// from normal sign in flow.
// |username_hash| is used to identify homedir mount point.
// Indicates that a user with the given `account_id` has just logged in.
// `username_hash` is used to identify homedir mount point.
// TODO(crbug.com/278643115): `browser_restart` and `is_child` is no longer
// used. Remove them.
virtual void UserLoggedIn(const AccountId& account_id,
const std::string& username_hash,
bool browser_restart,
bool is_child) = 0;
// If there's no user for the given `account_id`, a new is created with
// the given `user_type`. `is_ephemeral` is respected only if the `user_type`
// is either kRegular or kChild.
// If there's the user of `account_id` already (i.e. persisted),
// the user is kRegular or kChild, and the given `user_type` is either one,
// the type will be updated properly.
// Returns whether the new user is created.
virtual bool EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral) = 0;
// Called when the Profile instance for a user identified by `account_id`
// is created. `prefs` should be the one that is owned by Profile.
// The 'prefs' must be kept alive until OnUserProfileWillBeDestroyed

@ -273,56 +273,32 @@ void UserManagerImpl::UserLoggedIn(const AccountId& account_id,
last_session_active_account_id_initialized_ = true;
}
if (!logged_in_users_.empty()) {
// Handle multi sign-in case. For multi-sign in, there already should be
// active_user_ set, and the user to be logged in should be found
// in the persistent user list.
User* user = FindUserInListAndModify(account_id);
CHECK(active_user_);
CHECK(user);
bool is_primary_login = logged_in_users_.empty();
// For primary login case, i.e., there was no previous login, so there should
// be no active user. Otherwise, there was some previous login, so there
// should be an active user.
CHECK_EQ(!active_user_, is_primary_login);
OnUserLoggedIn(*user, username_hash);
// Reset the new user flag if the user already exists.
SetIsCurrentUserNew(false);
SendMultiUserSignInMetrics();
NotifyUserAddedToSession(user);
return;
}
// There are no logged in users. `active_user_` should not be set.
CHECK(!active_user_);
// Ensure User is there.
const UserType user_type =
CalculateUserType(account_id, FindUserInListAndModify(account_id),
browser_restart, is_child);
// Check whether the user should be ephemeral based on account_id.
// If this is for browser restarting, and new user needs to be created,
// that means, in precious chrome process, the user was ephemeral
// so disappeared (i.e. not in the persisted user list).
bool is_ephemeral = IsEphemeralAccountId(account_id) || browser_restart;
auto [user, created] = EnsureUser(account_id, user_type, is_ephemeral);
SetIsCurrentUserNew((created && (user->GetType() == UserType::kRegular ||
user->GetType() == UserType::kChild)) ||
user->GetType() == UserType::kPublicAccount);
User* user = FindUserAndModify(account_id);
CHECK(user);
OnUserLoggedIn(*user, username_hash);
OnPrimaryUserLoggedIn(*user);
OnActiveUserSwitched(*user);
local_state_->CommitPendingWrite();
NotifyOnLogin();
if (is_primary_login) {
OnPrimaryUserLoggedIn(*user);
OnActiveUserSwitched(*user);
local_state_->CommitPendingWrite();
NotifyOnLogin();
} else {
SendMultiUserSignInMetrics();
NotifyUserAddedToSession(user);
}
}
UserManagerImpl::EnsuredUser UserManagerImpl::EnsureUser(
const AccountId& account_id,
UserType user_type,
bool is_ephemeral) {
bool UserManagerImpl::EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral) {
User* user = FindUserInListAndModify(account_id);
bool created = user == nullptr;
switch (user_type) {
case UserType::kRegular:
case UserType::kChild:
@ -346,16 +322,17 @@ UserManagerImpl::EnsuredUser UserManagerImpl::EnsureUser(
} else {
user = AddGaiaUser(account_id, user_type);
}
break;
return true;
case UserType::kGuest:
CHECK(!user);
user = AddGuestUser();
break;
return true;
case UserType::kPublicAccount:
if (!user) {
user = AddPublicAccountUser(account_id);
return true;
}
break;
@ -369,7 +346,7 @@ UserManagerImpl::EnsuredUser UserManagerImpl::EnsureUser(
NOTREACHED() << "Unhandled usert type " << user_type;
}
CHECK(user);
return {user, created};
return false;
}
void UserManagerImpl::OnUserLoggedIn(User& user,

@ -154,6 +154,9 @@ class USER_MANAGER_EXPORT UserManagerImpl : public UserManager {
const std::string& user_id_hash,
bool browser_restart,
bool is_child) override;
bool EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral) override;
bool OnUserProfileCreated(const AccountId& account_id,
PrefService* prefs) override;
void OnUserProfileWillBeDestroyed(const AccountId& account_id) override;
@ -391,21 +394,6 @@ class USER_MANAGER_EXPORT UserManagerImpl : public UserManager {
// Subsequent calls have no effect. Must be called on the UI thread.
void EnsureUsersLoaded();
// If there's the user of `account_id` already (i.e. persisted), the user
// will be returned with `created` = false. If the user is kRegular or kChild,
// and given `user_type` is either one, the type will be updated properly.
// `is_ephemeral` param will be ignored in this case.
// If there's no such user, a new user of the given `user_type` will be
// created and returned with `created` = true. On creation, if the user
// type is kRegular or kChild, is_ephemeral is respected.
struct EnsuredUser {
raw_ptr<User> user;
bool created;
};
EnsuredUser EnsureUser(const AccountId& account_id,
UserType user_type,
bool is_ephemeral);
// Returns a list of users who have logged into this device previously.
// Same as GetUsers but used if you need to modify User from that list.
UserList& GetUsersAndModify();