// Copyright 2022 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/user_directory_integrity_manager.h"
#include <optional>
#include <utility>
#include "base/logging.h"
#include "base/notreached.h"
#include "base/values.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/account_id_util.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
namespace user_manager {
namespace {
// Initial version of the preference, contained String value of user's e-mail.
// This value is not written by the code, but we need to read it in case when
// device restart also resulted in applying OS update.
const char kUserDirectoryIntegrityAccountPref[] =
"incomplete_login_user_account";
// Updated version of the preference, contains a Dict that is used to serialize
// AccountId, and might contain additional information.
const char kUserDirectoryIntegrityAccountPrefV2[] =
"incomplete_login_user_account_v2";
const char kCleanupStrategyKey[] = "cleanup_strategy";
} // namespace
using CleanupStrategy = UserDirectoryIntegrityManager::CleanupStrategy;
UserDirectoryIntegrityManager::UserDirectoryIntegrityManager(
PrefService* local_state)
: local_state_(local_state) {}
UserDirectoryIntegrityManager::~UserDirectoryIntegrityManager() = default;
// static
void UserDirectoryIntegrityManager::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterStringPref(kUserDirectoryIntegrityAccountPref, {});
registry->RegisterDictionaryPref(kUserDirectoryIntegrityAccountPrefV2, {});
}
void UserDirectoryIntegrityManager::RecordCreatingNewUser(
const AccountId& account_id,
CleanupStrategy strategy) {
LOG(WARNING) << "Creating new user, don't have credentials yet.";
base::Value::Dict serialized_account;
StoreAccountId(account_id, serialized_account);
serialized_account.Set(kCleanupStrategyKey, static_cast<int>(strategy));
local_state_->SetDict(kUserDirectoryIntegrityAccountPrefV2,
std::move(serialized_account));
local_state_->CommitPendingWrite();
}
void UserDirectoryIntegrityManager::RemoveUser(const AccountId& account_id) {
UserManager::Get()->RemoveUser(account_id,
UserRemovalReason::MISCONFIGURED_USER);
}
void UserDirectoryIntegrityManager::ClearPrefs() {
LOG(WARNING) << "Created user have credentials now.";
local_state_->ClearPref(kUserDirectoryIntegrityAccountPref);
local_state_->ClearPref(kUserDirectoryIntegrityAccountPrefV2);
local_state_->CommitPendingWrite();
}
std::optional<AccountId>
UserDirectoryIntegrityManager::GetMisconfiguredUserAccountId() {
const base::Value::Dict& account_dict =
local_state_->GetDict(kUserDirectoryIntegrityAccountPrefV2);
std::optional<AccountId> result = LoadAccountId(account_dict);
if (result) {
return result;
}
return GetMisconfiguredUserAccountIdLegacy();
}
CleanupStrategy
UserDirectoryIntegrityManager::GetMisconfiguredUserCleanupStrategy() {
const base::Value::Dict& account_dict =
local_state_->GetDict(kUserDirectoryIntegrityAccountPrefV2);
std::optional<int> raw_strategy = account_dict.FindInt(kCleanupStrategyKey);
if (raw_strategy) {
CHECK(0 <= *raw_strategy);
CHECK(*raw_strategy <= static_cast<int>(CleanupStrategy::kMaxValue));
return static_cast<CleanupStrategy>(*raw_strategy);
}
// Default value
return CleanupStrategy::kRemoveUser;
}
std::optional<AccountId>
UserDirectoryIntegrityManager::GetMisconfiguredUserAccountIdLegacy() {
std::optional<std::string> misconfigured_user_email =
GetMisconfiguredUserEmail();
if (!misconfigured_user_email.has_value()) {
return std::nullopt;
}
UserList users = UserManager::Get()->GetUsers();
auto misconfigured_user_it =
base::ranges::find_if(users, [&misconfigured_user_email](User* user) {
return user->GetAccountId().GetUserEmail() ==
misconfigured_user_email.value();
});
if (misconfigured_user_it == std::end(users)) {
// If the user was not found in the list, then it's a regular user and not a
// Kiosk user, since regular misconfigured users are skipped during the
// loading process in `UserManagerBase::EnsureUsersLoaded`, to prevent
// showing them on the login screen
user_manager::KnownUser known_user(local_state_);
user_manager::CryptohomeId cryptohome_id(misconfigured_user_email.value());
return known_user.GetAccountIdByCryptohomeId(cryptohome_id);
}
if (User* misconfigured_user = *misconfigured_user_it;
misconfigured_user->IsDeviceLocalAccount() &&
misconfigured_user->IsKioskType()) {
return misconfigured_user->GetAccountId();
}
// Since we only record `incomplete_login_user_account` pref in
// `auth_session_authenticator` for regular and kiosk users, it should be
// impossible to reach here after checking for both types of users above.
NOTREACHED_IN_MIGRATION();
return std::nullopt;
}
std::optional<std::string>
UserDirectoryIntegrityManager::GetMisconfiguredUserEmail() {
auto incomplete_user_email =
local_state_->GetString(kUserDirectoryIntegrityAccountPref);
return incomplete_user_email.empty()
? std::nullopt
: std::make_optional(incomplete_user_email);
}
bool UserDirectoryIntegrityManager::IsUserMisconfigured(
const AccountId& account_id) {
const base::Value::Dict& account_dict =
local_state_->GetDict(kUserDirectoryIntegrityAccountPrefV2);
if (!account_dict.empty()) {
return AccountIdMatches(account_id, account_dict);
}
// Legacy option.
std::optional<std::string> incomplete_user_email =
GetMisconfiguredUserEmail();
return incomplete_user_email.has_value() &&
incomplete_user_email == account_id.GetUserEmail();
}
} // namespace user_manager