// Copyright 2014 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.h"
#include <stddef.h>
#include <memory>
#include "ash/constants/ash_pref_names.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace user_manager {
namespace {
// Must be in sync with histogram enum UserTypeChanged in enums.xml.
// The values must never be changed (only new ones can be added) as they
// are stored in UMA logs.
enum class UserTypeChangeHistogram {
UNKNOWN_FATAL = 0,
REGULAR_TO_CHILD = 1,
CHILD_TO_REGULAR = 2,
COUNT, // Not a value, just a count of other values.
};
void UMAUserTypeChanged(const UserTypeChangeHistogram value) {
UMA_HISTOGRAM_ENUMERATION("UserManager.UserTypeChanged", value,
UserTypeChangeHistogram::COUNT);
}
// Returns account name portion of an email.
std::string GetUserName(const std::string& email) {
std::string::size_type i = email.find('@');
if (i == 0 || i == std::string::npos) {
return email;
}
return email.substr(0, i);
}
} // namespace
// static
bool User::TypeHasGaiaAccount(UserType user_type) {
return user_type == UserType::kRegular || user_type == UserType::kChild;
}
// static
bool User::TypeIsKiosk(UserType type) {
return type == UserType::kKioskApp || type == UserType::kWebKioskApp;
}
User::User(const AccountId& account_id, UserType type)
: account_id_(account_id), type_(type), user_image_(new UserImage()) {
switch (type_) {
case user_manager::UserType::kRegular:
case user_manager::UserType::kChild:
case user_manager::UserType::kKioskApp:
case user_manager::UserType::kWebKioskApp:
set_display_email(account_id.GetUserEmail());
break;
case user_manager::UserType::kGuest:
case user_manager::UserType::kPublicAccount:
// Public accounts nor guest account do not have a real email address,
// so they do not set |display_email_|.
break;
}
}
User::~User() = default;
std::string User::GetDisplayEmail() const {
return display_email();
}
std::u16string User::GetDisplayName() const {
// Fallback to the email account name in case display name haven't been set.
return display_name_.empty() ? base::UTF8ToUTF16(GetAccountName(true))
: display_name_;
}
std::u16string User::GetGivenName() const {
return given_name_;
}
const gfx::ImageSkia& User::GetImage() const {
return user_image_->image();
}
const AccountId& User::GetAccountId() const {
return account_id_;
}
void User::UpdateType(UserType new_type) {
// Can only change between regular and child.
if ((type_ == user_manager::UserType::kChild ||
type_ == user_manager::UserType::kRegular) &&
(new_type == user_manager::UserType::kChild ||
new_type == user_manager::UserType::kRegular)) {
// We want all the other type changes to crash, that is why this check is
// not at the top level.
if (type_ == new_type) {
return;
}
LOG(WARNING) << "User type has changed: " << type_ << " -> " << new_type;
type_ = new_type;
UMAUserTypeChanged(new_type == user_manager::UserType::kChild
? UserTypeChangeHistogram::REGULAR_TO_CHILD
: UserTypeChangeHistogram::CHILD_TO_REGULAR);
return;
}
UMAUserTypeChanged(UserTypeChangeHistogram::UNKNOWN_FATAL);
LOG(FATAL) << "Unsupported user type change " << type_ << "=>" << new_type;
}
bool User::HasGaiaAccount() const {
return TypeHasGaiaAccount(GetType());
}
bool User::IsChild() const {
return GetType() == UserType::kChild;
}
std::string User::GetAccountName(bool use_display_email) const {
if (use_display_email && !display_email_.empty())
return GetUserName(display_email_);
else
return GetUserName(account_id_.GetUserEmail());
}
bool User::CanLock() const {
switch (type_) {
case user_manager::UserType::kRegular:
case user_manager::UserType::kChild:
if (!profile_prefs_) {
return false;
}
break;
case user_manager::UserType::kKioskApp:
case user_manager::UserType::kWebKioskApp:
case user_manager::UserType::kGuest:
return false;
case user_manager::UserType::kPublicAccount:
if (!profile_prefs_ ||
!profile_prefs_->GetBoolean(
ash::prefs::kLoginExtensionApiCanLockManagedGuestSession)) {
return false;
}
break;
}
return profile_prefs_->GetBoolean(ash::prefs::kAllowScreenLock);
}
std::string User::display_email() const {
return display_email_;
}
const std::string& User::username_hash() const {
return username_hash_;
}
bool User::is_logged_in() const {
return is_logged_in_;
}
bool User::is_active() const {
return is_active_;
}
bool User::has_gaia_account() const {
static_assert(static_cast<int>(user_manager::UserType::kMaxValue) == 9,
"kMaxValue should equal 9");
switch (GetType()) {
case user_manager::UserType::kRegular:
case user_manager::UserType::kChild:
return true;
case user_manager::UserType::kGuest:
case user_manager::UserType::kPublicAccount:
case user_manager::UserType::kKioskApp:
case user_manager::UserType::kWebKioskApp:
return false;
}
return false;
}
void User::AddProfileCreatedObserver(base::OnceClosure on_profile_created) {
if (profile_is_created_)
std::move(on_profile_created).Run();
else
on_profile_created_observers_.push_back(std::move(on_profile_created));
}
void User::SetProfileIsCreated() {
profile_is_created_ = true;
for (auto& callback : on_profile_created_observers_) {
std::move(callback).Run();
}
on_profile_created_observers_.clear();
}
bool User::IsAffiliated() const {
// Device local accounts are always affiliated.
if (IsDeviceLocalAccount()) {
return true;
}
return is_affiliated_.value_or(false);
}
void User::IsAffiliatedAsync(
base::OnceCallback<void(bool)> is_affiliated_callback) {
// TODO(b/278643115): Conceptually, we should call
// std::move(is_affiliated_callback).Run(true)
// here immediately if this is for device local account.
if (is_affiliated_.has_value()) {
std::move(is_affiliated_callback).Run(is_affiliated_.value());
} else {
on_affiliation_set_callbacks_.push_back(std::move(is_affiliated_callback));
}
}
void User::SetAffiliated(bool is_affiliated) {
// Device local accounts are always affiliated. No affiliation
// modification must happen.
CHECK(!IsDeviceLocalAccount());
is_affiliated_ = is_affiliated;
for (auto& callback : on_affiliation_set_callbacks_) {
std::move(callback).Run(is_affiliated_.value());
}
on_affiliation_set_callbacks_.clear();
}
bool User::IsDeviceLocalAccount() const {
switch (type_) {
case user_manager::UserType::kRegular:
case user_manager::UserType::kChild:
case user_manager::UserType::kGuest:
return false;
case user_manager::UserType::kPublicAccount:
case user_manager::UserType::kKioskApp:
case user_manager::UserType::kWebKioskApp:
return true;
}
return false;
}
bool User::IsKioskType() const {
return TypeIsKiosk(GetType());
}
User* User::CreateRegularUser(const AccountId& account_id,
const UserType type) {
CHECK(type == UserType::kRegular || type == UserType::kChild)
<< "Invalid user type " << type;
return new User(account_id, type);
}
User* User::CreateGuestUser(const AccountId& guest_account_id) {
return new User(guest_account_id, UserType::kGuest);
}
User* User::CreateKioskAppUser(const AccountId& kiosk_app_account_id) {
return new User(kiosk_app_account_id, UserType::kKioskApp);
}
User* User::CreateWebKioskAppUser(const AccountId& web_kiosk_account_id) {
return new User(web_kiosk_account_id, UserType::kWebKioskApp);
}
User* User::CreatePublicAccountUser(const AccountId& account_id,
bool is_using_saml) {
User* user = new User(account_id, UserType::kPublicAccount);
user->set_using_saml(is_using_saml);
return user;
}
void User::SetAccountLocale(const std::string& resolved_account_locale) {
account_locale_ = std::make_unique<std::string>(resolved_account_locale);
}
void User::SetImage(std::unique_ptr<UserImage> user_image, int image_index) {
user_image_ = std::move(user_image);
image_index_ = image_index;
image_is_stub_ = false;
image_is_loading_ = false;
}
void User::SetImageURL(const GURL& image_url) {
user_image_->set_url(image_url);
}
void User::SetStubImage(std::unique_ptr<UserImage> stub_user_image,
int image_index,
bool is_loading) {
user_image_ = std::move(stub_user_image);
image_index_ = image_index;
image_is_stub_ = true;
image_is_loading_ = is_loading;
}
} // namespace user_manager