blob: 86da0b950ef3685331aec16bdcc7bf38a78957ba [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/chromeos/login/screens/user_selection_screen.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/login/lock_screen_utils.h"
#include "chrome/browser/chromeos/login/quick_unlock/fingerprint_storage.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
#include "chrome/browser/chromeos/login/reauth_stats.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/ui/views/user_board_view.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/ash/login_screen_client.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/components/proximity_auth/screenlock_bridge.h"
#include "chromeos/components/proximity_auth/smart_lock_metrics_recorder.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/account_id/account_id.h"
#include "components/arc/arc_util.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/user_activity/user_activity_detector.h"
#include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
namespace chromeos {
namespace {
// User dictionary keys.
const char kKeyUsername[] = "username";
const char kKeyDisplayName[] = "displayName";
const char kKeyEmailAddress[] = "emailAddress";
const char kKeyEnterpriseDisplayDomain[] = "enterpriseDisplayDomain";
const char kKeyPublicAccount[] = "publicAccount";
const char kKeyLegacySupervisedUser[] = "legacySupervisedUser";
const char kKeyChildUser[] = "childUser";
const char kKeyDesktopUser[] = "isDesktopUser";
const char kKeySignedIn[] = "signedIn";
const char kKeyCanRemove[] = "canRemove";
const char kKeyIsOwner[] = "isOwner";
const char kKeyIsActiveDirectory[] = "isActiveDirectory";
const char kKeyInitialAuthType[] = "initialAuthType";
const char kKeyMultiProfilesAllowed[] = "isMultiProfilesAllowed";
const char kKeyMultiProfilesPolicy[] = "multiProfilesPolicy";
const char kKeyInitialLocales[] = "initialLocales";
const char kKeyInitialLocale[] = "initialLocale";
const char kKeyInitialMultipleRecommendedLocales[] =
"initialMultipleRecommendedLocales";
const char kKeyAllowFingerprint[] = "allowFingerprint";
// Max number of users to show.
// Please keep synced with one in signin_userlist_unittest.cc.
const size_t kMaxUsers = 18;
const int kPasswordClearTimeoutSec = 60;
// Returns true if we have enterprise domain information.
// |out_domain|: Output value of the enterprise domain.
bool GetEnterpriseDomain(std::string* out_domain) {
policy::BrowserPolicyConnectorChromeOS* policy_connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (policy_connector->IsCloudManaged()) {
*out_domain = policy_connector->GetEnterpriseDisplayDomain();
return true;
}
return false;
}
// Get locales information of public account user.
// Returns a list of available locales.
// |public_session_recommended_locales|: This can be nullptr if we don't have
// recommended locales.
// |out_selected_locale|: Output value of the initially selected locale.
// |out_multiple_locales|: Output value indicates whether we have multiple
// recommended locales.
std::unique_ptr<base::ListValue> GetPublicSessionLocales(
const std::vector<std::string>* public_session_recommended_locales,
std::string* out_selected_locale,
bool* out_multiple_locales) {
std::vector<std::string> kEmptyRecommendedLocales;
const std::vector<std::string>& recommended_locales =
public_session_recommended_locales ? *public_session_recommended_locales
: kEmptyRecommendedLocales;
// Construct the list of available locales. This list consists of the
// recommended locales, followed by all others.
std::unique_ptr<base::ListValue> available_locales =
GetUILanguageList(&recommended_locales, std::string());
// Select the the first recommended locale that is actually available or the
// current UI locale if none of them are available.
*out_selected_locale =
FindMostRelevantLocale(recommended_locales, *available_locales.get(),
g_browser_process->GetApplicationLocale());
*out_multiple_locales = recommended_locales.size() >= 2;
return available_locales;
}
void AddPublicSessionDetailsToUserDictionaryEntry(
base::DictionaryValue* user_dict,
const std::vector<std::string>* public_session_recommended_locales) {
std::string domain;
if (GetEnterpriseDomain(&domain))
user_dict->SetString(kKeyEnterpriseDisplayDomain, domain);
std::string selected_locale;
bool has_multiple_locales;
std::unique_ptr<base::ListValue> available_locales =
GetPublicSessionLocales(public_session_recommended_locales,
&selected_locale, &has_multiple_locales);
// Set |kKeyInitialLocales| to the list of available locales.
user_dict->Set(kKeyInitialLocales, std::move(available_locales));
// Set |kKeyInitialLocale| to the initially selected locale.
user_dict->SetString(kKeyInitialLocale, selected_locale);
// Set |kKeyInitialMultipleRecommendedLocales| to indicate whether the list
// of recommended locales contains at least two entries. This is used to
// decide whether the public session pod expands to its basic form (for zero
// or one recommended locales) or the advanced form (two or more recommended
// locales).
user_dict->SetBoolean(kKeyInitialMultipleRecommendedLocales,
has_multiple_locales);
}
// Determines the initial fingerprint state for the given user.
ash::mojom::FingerprintState GetInitialFingerprintState(
const user_manager::User* user) {
// User must be logged in.
if (!user->is_logged_in())
return ash::mojom::FingerprintState::UNAVAILABLE;
// Quick unlock storage must be available.
quick_unlock::QuickUnlockStorage* quick_unlock_storage =
quick_unlock::QuickUnlockFactory::GetForUser(user);
if (!quick_unlock_storage)
return ash::mojom::FingerprintState::UNAVAILABLE;
// Fingerprint is not registered for this account.
if (!quick_unlock_storage->fingerprint_storage()->HasRecord())
return ash::mojom::FingerprintState::UNAVAILABLE;
// Fingerprint unlock attempts should not be exceeded, as the lock screen has
// not been displayed yet.
DCHECK(
!quick_unlock_storage->fingerprint_storage()->ExceededUnlockAttempts());
// It has been too long since the last authentication.
if (!quick_unlock_storage->HasStrongAuth())
return ash::mojom::FingerprintState::DISABLED_FROM_TIMEOUT;
// Auth is available.
if (quick_unlock_storage->IsFingerprintAuthenticationAvailable())
return ash::mojom::FingerprintState::AVAILABLE;
// Default to unavailabe.
return ash::mojom::FingerprintState::UNAVAILABLE;
}
// Returns true if dircrypto migration check should be performed.
bool ShouldCheckNeedDircryptoMigration() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableEncryptionMigration) &&
arc::IsArcAvailable();
}
// Returns true if the user can run ARC based on the user type.
bool IsUserAllowedForARC(const AccountId& account_id) {
return user_manager::UserManager::IsInitialized() &&
arc::IsArcAllowedForUser(
user_manager::UserManager::Get()->FindUser(account_id));
}
AccountId GetOwnerAccountId() {
std::string owner_email;
chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner,
&owner_email);
const AccountId owner = user_manager::known_user::GetAccountId(
owner_email, std::string() /* id */, AccountType::UNKNOWN);
return owner;
}
bool IsEnterpriseManaged() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
return connector->IsEnterpriseManaged();
}
bool IsSigninToAdd() {
return LoginDisplayHost::default_host() &&
user_manager::UserManager::Get()->IsUserLoggedIn();
}
bool CanRemoveUser(const user_manager::User* user) {
const bool is_single_user =
user_manager::UserManager::Get()->GetUsers().size() == 1;
// Single user check here is necessary because owner info might not be
// available when running into login screen on first boot.
// See http://crosbug.com/12723
if (is_single_user && !IsEnterpriseManaged())
return false;
if (!user->GetAccountId().is_valid())
return false;
if (user->GetAccountId() == GetOwnerAccountId())
return false;
if (user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT ||
user->is_logged_in() || IsSigninToAdd())
return false;
return true;
}
void GetMultiProfilePolicy(const user_manager::User* user,
bool* out_is_allowed,
ash::mojom::MultiProfileUserBehavior* out_policy) {
const std::string& user_id = user->GetAccountId().GetUserEmail();
MultiProfileUserController* multi_profile_user_controller =
ChromeUserManager::Get()->GetMultiProfileUserController();
MultiProfileUserController::UserAllowedInSessionReason is_user_allowed_reason;
*out_is_allowed = multi_profile_user_controller->IsUserAllowedInSession(
user_id, &is_user_allowed_reason);
std::string policy;
if (is_user_allowed_reason ==
MultiProfileUserController::NOT_ALLOWED_OWNER_AS_SECONDARY) {
policy = MultiProfileUserController::kBehaviorOwnerPrimaryOnly;
} else {
policy = multi_profile_user_controller->GetCachedValue(user_id);
}
*out_policy = MultiProfileUserController::UserBehaviorStringToEnum(policy);
}
} // namespace
// Helper class to call cryptohome to check whether a user needs dircrypto
// migration. The check results are cached to limit calls to cryptohome.
class UserSelectionScreen::DircryptoMigrationChecker {
public:
explicit DircryptoMigrationChecker(UserSelectionScreen* owner)
: owner_(owner), weak_ptr_factory_(this) {}
~DircryptoMigrationChecker() = default;
// Start to check whether the given user needs dircrypto migration.
void Check(const AccountId& account_id) {
focused_user_ = account_id;
// If the user may be enterprise-managed, don't display the banner, because
// migration may be blocked by user policy (and user policy is not available
// at this time yet).
if (!policy::BrowserPolicyConnector::IsNonEnterpriseUser(
account_id.GetUserEmail())) {
UpdateUI(account_id, false);
return;
}
auto it = needs_dircrypto_migration_cache_.find(account_id);
if (it != needs_dircrypto_migration_cache_.end()) {
UpdateUI(account_id, it->second);
return;
}
// No banner if the user is not allowed for ARC.
if (!IsUserAllowedForARC(account_id)) {
UpdateUI(account_id, false);
return;
}
CryptohomeClient::Get()->WaitForServiceToBeAvailable(
base::Bind(&DircryptoMigrationChecker::RunCryptohomeCheck,
weak_ptr_factory_.GetWeakPtr(), account_id));
}
private:
// WaitForServiceToBeAvailable callback to invoke NeedsDircryptoMigration when
// cryptohome service is available.
void RunCryptohomeCheck(const AccountId& account_id, bool service_is_ready) {
if (!service_is_ready) {
LOG(ERROR) << "Cryptohome is not available.";
return;
}
CryptohomeClient::Get()->NeedsDircryptoMigration(
cryptohome::CreateAccountIdentifierFromAccountId(account_id),
base::BindOnce(&DircryptoMigrationChecker::
OnCryptohomeNeedsDircryptoMigrationCallback,
weak_ptr_factory_.GetWeakPtr(), account_id));
}
// Callback invoked when NeedsDircryptoMigration call is finished.
void OnCryptohomeNeedsDircryptoMigrationCallback(
const AccountId& account_id,
base::Optional<bool> needs_migration) {
if (!needs_migration.has_value()) {
LOG(ERROR) << "Failed to call cryptohome NeedsDircryptoMigration.";
// Hide the banner to avoid confusion in http://crbug.com/721948.
// Cache is not updated so that cryptohome call will still be attempted.
UpdateUI(account_id, false);
return;
}
needs_dircrypto_migration_cache_[account_id] = needs_migration.value();
UpdateUI(account_id, needs_migration.value());
}
// Update UI for the given user when the check result is available.
void UpdateUI(const AccountId& account_id, bool needs_migration) {
// Bail if the user is not the currently focused.
if (account_id != focused_user_)
return;
owner_->ShowBannerMessage(
needs_migration ? l10n_util::GetStringUTF16(
IDS_LOGIN_NEEDS_DIRCRYPTO_MIGRATION_BANNER)
: base::string16(),
needs_migration);
}
UserSelectionScreen* const owner_;
AccountId focused_user_ = EmptyAccountId();
// Cached result of NeedsDircryptoMigration cryptohome check. Key is the
// account id of users. True value means the user needs dircrypto migration
// and false means dircrypto migration is done.
std::map<AccountId, bool> needs_dircrypto_migration_cache_;
base::WeakPtrFactory<DircryptoMigrationChecker> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DircryptoMigrationChecker);
};
UserSelectionScreen::UserSelectionScreen(const std::string& display_type)
: BaseScreen(OobeScreen::SCREEN_USER_SELECTION),
display_type_(display_type),
weak_factory_(this) {}
UserSelectionScreen::~UserSelectionScreen() {
proximity_auth::ScreenlockBridge::Get()->SetLockHandler(nullptr);
ui::UserActivityDetector* activity_detector = ui::UserActivityDetector::Get();
if (activity_detector && activity_detector->HasObserver(this))
activity_detector->RemoveObserver(this);
}
void UserSelectionScreen::InitEasyUnlock() {
proximity_auth::ScreenlockBridge::Get()->SetLockHandler(this);
}
// static
void UserSelectionScreen::FillUserDictionary(
const user_manager::User* user,
bool is_owner,
bool is_signin_to_add,
proximity_auth::mojom::AuthType auth_type,
const std::vector<std::string>* public_session_recommended_locales,
base::DictionaryValue* user_dict) {
const bool is_public_session =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
const bool is_legacy_supervised_user =
user->GetType() == user_manager::USER_TYPE_SUPERVISED;
const bool is_child_user = user->GetType() == user_manager::USER_TYPE_CHILD;
user_dict->SetString(kKeyUsername, user->GetAccountId().Serialize());
user_dict->SetString(kKeyEmailAddress, user->display_email());
user_dict->SetString(kKeyDisplayName, user->GetDisplayName());
user_dict->SetBoolean(kKeyPublicAccount, is_public_session);
user_dict->SetBoolean(kKeyLegacySupervisedUser, is_legacy_supervised_user);
user_dict->SetBoolean(kKeyChildUser, is_child_user);
user_dict->SetBoolean(kKeyDesktopUser, false);
user_dict->SetInteger(kKeyInitialAuthType, static_cast<int>(auth_type));
user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
user_dict->SetBoolean(kKeyIsOwner, is_owner);
user_dict->SetBoolean(kKeyIsActiveDirectory, user->IsActiveDirectoryUser());
user_dict->SetBoolean(kKeyAllowFingerprint,
GetInitialFingerprintState(user) ==
ash::mojom::FingerprintState::AVAILABLE);
FillMultiProfileUserPrefs(user, user_dict, is_signin_to_add);
if (is_public_session) {
AddPublicSessionDetailsToUserDictionaryEntry(
user_dict, public_session_recommended_locales);
}
}
// static
void UserSelectionScreen::FillMultiProfileUserPrefs(
const user_manager::User* user,
base::DictionaryValue* user_dict,
bool is_signin_to_add) {
if (!is_signin_to_add) {
user_dict->SetBoolean(kKeyMultiProfilesAllowed, true);
return;
}
bool is_user_allowed;
ash::mojom::MultiProfileUserBehavior policy;
GetMultiProfilePolicy(user, &is_user_allowed, &policy);
user_dict->SetBoolean(kKeyMultiProfilesAllowed, is_user_allowed);
user_dict->SetInteger(kKeyMultiProfilesPolicy, static_cast<int>(policy));
}
// static
bool UserSelectionScreen::ShouldForceOnlineSignIn(
const user_manager::User* user) {
// Public sessions are always allowed to log in offline.
// Supervised users are always allowed to log in offline.
// For all other users, force online sign in if:
// * The flag to force online sign-in is set for the user.
// * The user's OAuth token is invalid or unknown.
if (user->is_logged_in())
return false;
const user_manager::User::OAuthTokenStatus token_status =
user->oauth_token_status();
const bool is_supervised_user =
user->GetType() == user_manager::USER_TYPE_SUPERVISED;
const bool is_public_session =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
const bool has_gaia_account = user->HasGaiaAccount();
if (is_supervised_user)
return false;
if (is_public_session)
return false;
// At this point the reason for invalid token should be already set. If not,
// this might be a leftover from an old version.
if (has_gaia_account &&
token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID)
RecordReauthReason(user->GetAccountId(), ReauthReason::OTHER);
// We need to force an online signin if the user is marked as requiring it or
// if there's an invalid OAUTH token that needs to be refreshed.
return user->force_online_signin() ||
(has_gaia_account &&
(token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID ||
token_status == user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN));
}
// static
ash::mojom::UserAvatarPtr UserSelectionScreen::BuildMojoUserAvatarForUser(
const user_manager::User* user) {
auto avatar = ash::mojom::UserAvatar::New();
if (!user->GetImage().isNull()) {
avatar->image = user->GetImage();
} else {
avatar->image = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_LOGIN_DEFAULT_USER);
}
// TODO(jdufault): Unify image handling between this code and
// user_image_source::GetUserImageInternal.
auto load_image_from_resource = [&](int resource_id) {
auto& rb = ui::ResourceBundle::GetSharedInstance();
base::StringPiece avatar_data =
rb.GetRawDataResourceForScale(resource_id, rb.GetMaxScaleFactor());
avatar->bytes.assign(avatar_data.begin(), avatar_data.end());
};
if (user->has_image_bytes()) {
avatar->bytes.assign(
user->image_bytes()->front(),
user->image_bytes()->front() + user->image_bytes()->size());
} else if (user->HasDefaultImage()) {
int resource_id = chromeos::default_user_image::kDefaultImageResourceIDs
[user->image_index()];
load_image_from_resource(resource_id);
} else if (user->image_is_stub()) {
load_image_from_resource(IDR_LOGIN_DEFAULT_USER);
}
return avatar;
}
void UserSelectionScreen::SetHandler(LoginDisplayWebUIHandler* handler) {
handler_ = handler;
if (handler_) {
// Forcibly refresh all of the user images, as the |handler_| instance may
// have been reused.
for (user_manager::User* user : users_)
handler_->OnUserImageChanged(*user);
}
}
void UserSelectionScreen::SetView(UserBoardView* view) {
view_ = view;
}
void UserSelectionScreen::Init(const user_manager::UserList& users) {
users_ = users;
ui::UserActivityDetector* activity_detector = ui::UserActivityDetector::Get();
if (activity_detector && !activity_detector->HasObserver(this))
activity_detector->AddObserver(this);
}
void UserSelectionScreen::OnBeforeUserRemoved(const AccountId& account_id) {
for (auto it = users_.cbegin(); it != users_.cend(); ++it) {
if ((*it)->GetAccountId() == account_id) {
users_.erase(it);
break;
}
}
}
void UserSelectionScreen::OnUserRemoved(const AccountId& account_id) {
if (!handler_)
return;
handler_->OnUserRemoved(account_id, users_.empty());
}
void UserSelectionScreen::OnUserImageChanged(const user_manager::User& user) {
if (!handler_)
return;
handler_->OnUserImageChanged(user);
// TODO(antrim) : updateUserImage(user.email())
}
void UserSelectionScreen::OnPasswordClearTimerExpired() {
if (handler_)
handler_->ClearUserPodPassword();
}
void UserSelectionScreen::OnUserActivity(const ui::Event* event) {
if (!password_clear_timer_.IsRunning()) {
password_clear_timer_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(kPasswordClearTimeoutSec), this,
&UserSelectionScreen::OnPasswordClearTimerExpired);
}
password_clear_timer_.Reset();
}
// static
const user_manager::UserList UserSelectionScreen::PrepareUserListForSending(
const user_manager::UserList& users,
const AccountId& owner,
bool is_signin_to_add) {
user_manager::UserList users_to_send;
bool has_owner = owner.is_valid();
size_t max_non_owner_users = has_owner ? kMaxUsers - 1 : kMaxUsers;
size_t non_owner_count = 0;
for (user_manager::User* user : users) {
bool is_owner = user->GetAccountId() == owner;
bool is_public_account =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
if ((is_public_account && !is_signin_to_add) || is_owner ||
(!is_public_account && non_owner_count < max_non_owner_users)) {
if (!is_owner)
++non_owner_count;
if (is_owner && users_to_send.size() > kMaxUsers) {
// Owner is always in the list.
users_to_send.insert(users_to_send.begin() + (kMaxUsers - 1), user);
while (users_to_send.size() > kMaxUsers)
users_to_send.erase(users_to_send.begin() + kMaxUsers);
} else if (users_to_send.size() < kMaxUsers) {
users_to_send.push_back(user);
}
}
}
return users_to_send;
}
void UserSelectionScreen::SendUserList() {
std::unique_ptr<base::ListValue> users_list =
UpdateAndReturnUserListForWebUI();
handler_->LoadUsers(users_to_send_, *users_list);
}
void UserSelectionScreen::HandleGetUsers() {
SendUserList();
}
void UserSelectionScreen::CheckUserStatus(const AccountId& account_id) {
// No checks on lock screen.
if (ScreenLocker::default_screen_locker())
return;
if (!token_handle_util_.get()) {
token_handle_util_.reset(new TokenHandleUtil());
}
if (token_handle_util_->HasToken(account_id)) {
token_handle_util_->CheckToken(
account_id, base::Bind(&UserSelectionScreen::OnUserStatusChecked,
weak_factory_.GetWeakPtr()));
}
// Run dircrypto migration check only on the login screen when necessary.
if (display_type_ == OobeUI::kLoginDisplay &&
ShouldCheckNeedDircryptoMigration()) {
if (!dircrypto_migration_checker_) {
dircrypto_migration_checker_ =
std::make_unique<DircryptoMigrationChecker>(this);
}
dircrypto_migration_checker_->Check(account_id);
}
}
void UserSelectionScreen::OnUserStatusChecked(
const AccountId& account_id,
TokenHandleUtil::TokenHandleStatus status) {
if (status == TokenHandleUtil::INVALID) {
RecordReauthReason(account_id, ReauthReason::INVALID_TOKEN_HANDLE);
token_handle_util_->MarkHandleInvalid(account_id);
SetAuthType(account_id, proximity_auth::mojom::AuthType::ONLINE_SIGN_IN,
base::string16());
}
}
// EasyUnlock stuff
void UserSelectionScreen::SetAuthType(const AccountId& account_id,
proximity_auth::mojom::AuthType auth_type,
const base::string16& initial_value) {
if (GetAuthType(account_id) ==
proximity_auth::mojom::AuthType::FORCE_OFFLINE_PASSWORD) {
return;
}
DCHECK(GetAuthType(account_id) !=
proximity_auth::mojom::AuthType::FORCE_OFFLINE_PASSWORD ||
auth_type == proximity_auth::mojom::AuthType::FORCE_OFFLINE_PASSWORD);
user_auth_type_map_[account_id] = auth_type;
view_->SetAuthType(account_id, auth_type, initial_value);
}
proximity_auth::mojom::AuthType UserSelectionScreen::GetAuthType(
const AccountId& account_id) const {
if (user_auth_type_map_.find(account_id) == user_auth_type_map_.end())
return proximity_auth::mojom::AuthType::OFFLINE_PASSWORD;
return user_auth_type_map_.find(account_id)->second;
}
proximity_auth::ScreenlockBridge::LockHandler::ScreenType
UserSelectionScreen::GetScreenType() const {
if (display_type_ == OobeUI::kLockDisplay)
return LOCK_SCREEN;
if (display_type_ == OobeUI::kLoginDisplay)
return SIGNIN_SCREEN;
return OTHER_SCREEN;
}
void UserSelectionScreen::ShowBannerMessage(const base::string16& message,
bool is_warning) {
view_->ShowBannerMessage(message, is_warning);
}
void UserSelectionScreen::ShowUserPodCustomIcon(
const AccountId& account_id,
const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
icon_options) {
view_->ShowUserPodCustomIcon(account_id, icon_options);
}
void UserSelectionScreen::HideUserPodCustomIcon(const AccountId& account_id) {
view_->HideUserPodCustomIcon(account_id);
}
void UserSelectionScreen::EnableInput() {
// If Easy Unlock fails to unlock the screen, re-enable the password input.
// This is only necessary on the lock screen, because the error handling for
// the sign-in screen uses a different code path.
if (ScreenLocker::default_screen_locker())
ScreenLocker::default_screen_locker()->EnableInput();
}
void UserSelectionScreen::Unlock(const AccountId& account_id) {
DCHECK_EQ(GetScreenType(), LOCK_SCREEN);
ScreenLocker::Hide();
}
void UserSelectionScreen::AttemptEasySignin(const AccountId& account_id,
const std::string& secret,
const std::string& key_label) {
DCHECK_EQ(GetScreenType(), SIGNIN_SCREEN);
const user_manager::User* const user =
user_manager::UserManager::Get()->FindUser(account_id);
DCHECK(user);
UserContext user_context(*user);
user_context.SetAuthFlow(UserContext::AUTH_FLOW_EASY_UNLOCK);
user_context.SetKey(Key(secret));
user_context.GetKey()->SetLabel(key_label);
// LoginDisplayHost does not exist in views-based lock screen.
if (LoginDisplayHost::default_host()) {
LoginDisplayHost::default_host()->GetLoginDisplay()->delegate()->Login(
user_context, SigninSpecifics());
} else {
SmartLockMetricsRecorder::RecordAuthResultSignInFailure(
SmartLockMetricsRecorder::SmartLockAuthResultFailureReason::
kLoginDisplayHostDoesNotExist);
}
}
void UserSelectionScreen::Show() {}
void UserSelectionScreen::Hide() {}
void UserSelectionScreen::HardLockPod(const AccountId& account_id) {
view_->SetAuthType(account_id,
proximity_auth::mojom::AuthType::OFFLINE_PASSWORD,
base::string16());
EasyUnlockService* service = GetEasyUnlockServiceForUser(account_id);
if (!service)
return;
service->SetHardlockState(EasyUnlockScreenlockStateHandler::USER_HARDLOCK);
}
void UserSelectionScreen::AttemptEasyUnlock(const AccountId& account_id) {
EasyUnlockService* service = GetEasyUnlockServiceForUser(account_id);
if (!service)
return;
service->AttemptAuth(account_id);
}
std::unique_ptr<base::ListValue>
UserSelectionScreen::UpdateAndReturnUserListForWebUI() {
std::unique_ptr<base::ListValue> users_list =
std::make_unique<base::ListValue>();
// TODO(nkostylev): Move to a separate method in UserManager.
// http://crbug.com/230852
const AccountId owner = GetOwnerAccountId();
const bool is_signin_to_add = IsSigninToAdd();
users_to_send_ = PrepareUserListForSending(users_, owner, is_signin_to_add);
user_auth_type_map_.clear();
for (const user_manager::User* user : users_to_send_) {
const AccountId& account_id = user->GetAccountId();
bool is_owner = (account_id == owner);
const bool is_public_account =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
const proximity_auth::mojom::AuthType initial_auth_type =
is_public_account
? proximity_auth::mojom::AuthType::EXPAND_THEN_USER_CLICK
: (ShouldForceOnlineSignIn(user)
? proximity_auth::mojom::AuthType::ONLINE_SIGN_IN
: proximity_auth::mojom::AuthType::OFFLINE_PASSWORD);
user_auth_type_map_[account_id] = initial_auth_type;
auto user_dict = std::make_unique<base::DictionaryValue>();
const std::vector<std::string>* public_session_recommended_locales =
public_session_recommended_locales_.find(account_id) ==
public_session_recommended_locales_.end()
? nullptr
: &public_session_recommended_locales_[account_id];
FillUserDictionary(user, is_owner, is_signin_to_add, initial_auth_type,
public_session_recommended_locales, user_dict.get());
user_dict->SetBoolean(kKeyCanRemove, CanRemoveUser(user));
users_list->Append(std::move(user_dict));
}
return users_list;
}
std::vector<ash::mojom::LoginUserInfoPtr>
UserSelectionScreen::UpdateAndReturnUserListForMojo() {
std::vector<ash::mojom::LoginUserInfoPtr> user_info_list;
const AccountId owner = GetOwnerAccountId();
const bool is_signin_to_add = IsSigninToAdd();
users_to_send_ = PrepareUserListForSending(users_, owner, is_signin_to_add);
user_auth_type_map_.clear();
for (const user_manager::User* user : users_to_send_) {
const AccountId& account_id = user->GetAccountId();
bool is_owner = owner == account_id;
const bool is_public_account =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
const proximity_auth::mojom::AuthType initial_auth_type =
is_public_account
? proximity_auth::mojom::AuthType::EXPAND_THEN_USER_CLICK
: (ShouldForceOnlineSignIn(user)
? proximity_auth::mojom::AuthType::ONLINE_SIGN_IN
: proximity_auth::mojom::AuthType::OFFLINE_PASSWORD);
user_auth_type_map_[account_id] = initial_auth_type;
ash::mojom::LoginUserInfoPtr user_info = ash::mojom::LoginUserInfo::New();
user_info->basic_user_info = ash::mojom::UserInfo::New();
user_info->basic_user_info->type = user->GetType();
user_info->basic_user_info->account_id = user->GetAccountId();
user_info->basic_user_info->display_name =
base::UTF16ToUTF8(user->GetDisplayName());
user_info->basic_user_info->display_email = user->display_email();
user_info->basic_user_info->avatar = BuildMojoUserAvatarForUser(user);
user_info->auth_type = initial_auth_type;
user_info->is_signed_in = user->is_logged_in();
user_info->is_device_owner = is_owner;
user_info->can_remove = CanRemoveUser(user);
user_info->fingerprint_state = GetInitialFingerprintState(user);
// Fill multi-profile data.
if (!is_signin_to_add) {
user_info->is_multiprofile_allowed = true;
} else {
GetMultiProfilePolicy(user, &user_info->is_multiprofile_allowed,
&user_info->multiprofile_policy);
}
// Fill public session data.
if (user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
user_info->public_account_info = ash::mojom::PublicAccountInfo::New();
std::string domain;
if (GetEnterpriseDomain(&domain))
user_info->public_account_info->enterprise_domain = domain;
const std::vector<std::string>* public_session_recommended_locales =
public_session_recommended_locales_.find(account_id) ==
public_session_recommended_locales_.end()
? nullptr
: &public_session_recommended_locales_[account_id];
std::string selected_locale;
bool has_multiple_locales;
std::unique_ptr<base::ListValue> available_locales =
GetPublicSessionLocales(public_session_recommended_locales,
&selected_locale, &has_multiple_locales);
DCHECK(available_locales);
user_info->public_account_info->available_locales =
lock_screen_utils::FromListValueToLocaleItem(
std::move(available_locales));
user_info->public_account_info->default_locale = selected_locale;
user_info->public_account_info->show_advanced_view = has_multiple_locales;
// Do not show expanded view when in demo mode.
user_info->public_account_info->show_expanded_view =
!DemoSession::IsDeviceInDemoMode();
}
user_info->can_remove = CanRemoveUser(user);
// Send a request to get keyboard layouts for default locale.
if (is_public_account && LoginScreenClient::HasInstance()) {
LoginScreenClient::Get()->RequestPublicSessionKeyboardLayouts(
account_id, user_info->public_account_info->default_locale);
}
user_info_list.push_back(std::move(user_info));
}
return user_info_list;
}
void UserSelectionScreen::SetUsersLoaded(bool loaded) {
users_loaded_ = loaded;
}
EasyUnlockService* UserSelectionScreen::GetEasyUnlockServiceForUser(
const AccountId& account_id) const {
if (GetScreenType() == OTHER_SCREEN)
return nullptr;
const user_manager::User* unlock_user = nullptr;
for (const user_manager::User* user : users_) {
if (user->GetAccountId() == account_id) {
unlock_user = user;
break;
}
}
if (!unlock_user)
return nullptr;
ProfileHelper* profile_helper = ProfileHelper::Get();
Profile* profile = profile_helper->GetProfileByUser(unlock_user);
// The user profile should exist if and only if this is the lock screen.
DCHECK_EQ(!!profile, GetScreenType() == LOCK_SCREEN);
if (!profile)
profile = profile_helper->GetSigninProfile();
return EasyUnlockService::Get(profile);
}
} // namespace chromeos