blob: 15647052bd5b68f4b4a2430c4f8bd84512ab22b9 [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 <utility>
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/login/quick_unlock/pin_storage.h"
#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_factory.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/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/signin/easy_unlock_service.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "components/prefs/pref_service.h"
#include "components/proximity_auth/screenlock_bridge.h"
#include "components/signin/core/account_id/account_id.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/user_activity/user_activity_detector.h"
namespace chromeos {
namespace {
// User dictionary keys.
const char kKeyUsername[] = "username";
const char kKeyGaiaID[] = "gaiaId";
const char kKeyDisplayName[] = "displayName";
const char kKeyEmailAddress[] = "emailAddress";
const char kKeyEnterpriseDomain[] = "enterpriseDomain";
const char kKeyPublicAccount[] = "publicAccount";
const char kKeyLegacySupervisedUser[] = "legacySupervisedUser";
const char kKeyChildUser[] = "childUser";
const char kKeyDesktopUser[] = "isDesktopUser";
const char kKeyShowPin[] = "showPin";
const char kKeySignedIn[] = "signedIn";
const char kKeyCanRemove[] = "canRemove";
const char kKeyIsOwner[] = "isOwner";
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 kKeyInitialKeyboardLayout[] = "initialKeyboardLayout";
// 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;
void AddPublicSessionDetailsToUserDictionaryEntry(
base::DictionaryValue* user_dict,
const std::vector<std::string>* public_session_recommended_locales) {
policy::BrowserPolicyConnectorChromeOS* policy_connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (policy_connector->IsEnterpriseManaged()) {
user_dict->SetString(kKeyEnterpriseDomain,
policy_connector->GetEnterpriseDomain());
}
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.
const std::string selected_locale = FindMostRelevantLocale(
recommended_locales,
*available_locales.get(),
g_browser_process->GetApplicationLocale());
// Set |kKeyInitialLocales| to the list of available locales.
user_dict->Set(kKeyInitialLocales, available_locales.release());
// 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,
recommended_locales.size() >= 2);
// Set |kKeyInitialKeyboardLayout| to the current keyboard layout. This
// value will be used temporarily only because the UI immediately requests a
// list of keyboard layouts suitable for the currently selected locale.
user_dict->Set(kKeyInitialKeyboardLayout,
GetCurrentKeyboardLayout().release());
}
// Returns true if the PIN keyboard should be displayed for the given |user|.
bool CanShowPinForUser(user_manager::User* user) {
if (!user->is_logged_in())
return false;
PinStorage* pin_storage = PinStorageFactory::GetForUser(user);
if (!pin_storage)
return false;
return pin_storage->IsPinAuthenticationAvailable();
}
} // namespace
UserSelectionScreen::UserSelectionScreen(const std::string& display_type)
: handler_(nullptr),
login_display_delegate_(nullptr),
view_(nullptr),
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);
}
void UserSelectionScreen::SetLoginDisplayDelegate(
LoginDisplay::Delegate* login_display_delegate) {
login_display_delegate_ = login_display_delegate;
}
// static
void UserSelectionScreen::FillUserDictionary(
user_manager::User* user,
bool is_owner,
bool is_signin_to_add,
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, auth_type);
user_dict->SetBoolean(kKeyShowPin, CanShowPinForUser(user));
user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
user_dict->SetBoolean(kKeyIsOwner, is_owner);
FillMultiProfileUserPrefs(user, user_dict, is_signin_to_add);
FillKnownUserPrefs(user, user_dict);
if (is_public_session) {
AddPublicSessionDetailsToUserDictionaryEntry(
user_dict, public_session_recommended_locales);
}
}
// static
void UserSelectionScreen::FillKnownUserPrefs(user_manager::User* user,
base::DictionaryValue* user_dict) {
std::string gaia_id;
if (user_manager::known_user::FindGaiaID(user->GetAccountId(), &gaia_id)) {
user_dict->SetString(kKeyGaiaID, gaia_id);
}
}
// static
void UserSelectionScreen::FillMultiProfileUserPrefs(
user_manager::User* user,
base::DictionaryValue* user_dict,
bool is_signin_to_add) {
const std::string& user_id = user->email();
if (is_signin_to_add) {
MultiProfileUserController* multi_profile_user_controller =
ChromeUserManager::Get()->GetMultiProfileUserController();
MultiProfileUserController::UserAllowedInSessionReason isUserAllowedReason;
bool isUserAllowed = multi_profile_user_controller->IsUserAllowedInSession(
user_id, &isUserAllowedReason);
user_dict->SetBoolean(kKeyMultiProfilesAllowed, isUserAllowed);
std::string behavior;
switch (isUserAllowedReason) {
case MultiProfileUserController::NOT_ALLOWED_OWNER_AS_SECONDARY:
behavior = MultiProfileUserController::kBehaviorOwnerPrimaryOnly;
break;
default:
behavior = multi_profile_user_controller->GetCachedValue(user_id);
}
user_dict->SetString(kKeyMultiProfilesPolicy, behavior);
} else {
user_dict->SetBoolean(kKeyMultiProfilesAllowed, true);
}
}
// static
bool UserSelectionScreen::ShouldForceOnlineSignIn(
const user_manager::User* user) {
// Public sessions are always allowed to log in offline.
// Supervised user are allowed to log in offline if their OAuth token status
// is unknown or valid.
// 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.
// * The user's OAuth token status is unknown (except supervised users,
// see above).
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;
if (is_supervised_user &&
token_status == user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN) {
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 (token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID)
RecordReauthReason(user->GetAccountId(), ReauthReason::OTHER);
return user->force_online_signin() ||
(token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID) ||
(token_status == user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
}
void UserSelectionScreen::SetHandler(LoginDisplayWebUIHandler* handler) {
handler_ = handler;
}
void UserSelectionScreen::SetView(UserBoardView* view) {
view_ = view;
}
void UserSelectionScreen::Init(const user_manager::UserList& users,
bool show_guest) {
users_ = users;
show_guest_ = show_guest;
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 (user_manager::UserList::iterator it = users_.begin(); it != users_.end();
++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::UserList::const_iterator it = users.begin();
it != users.end();
++it) {
bool is_owner = ((*it)->GetAccountId() == owner);
bool is_public_account =
((*it)->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), *it);
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(*it);
}
}
}
return users_to_send;
}
void UserSelectionScreen::SendUserList() {
base::ListValue users_list;
// TODO(nkostylev): Move to a separate method in UserManager.
// http://crbug.com/230852
bool single_user = users_.size() == 1;
bool is_signin_to_add = LoginDisplayHost::default_host() &&
user_manager::UserManager::Get()->IsUserLoggedIn();
std::string owner_email;
chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner,
&owner_email);
const AccountId owner =
user_manager::known_user::GetAccountId(owner_email, std::string());
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
bool is_enterprise_managed = connector->IsEnterpriseManaged();
const user_manager::UserList users_to_send =
PrepareUserListForSending(users_, owner, is_signin_to_add);
user_auth_type_map_.clear();
const std::vector<std::string> kEmptyRecommendedLocales;
for (user_manager::UserList::const_iterator it = users_to_send.begin();
it != users_to_send.end();
++it) {
const AccountId& account_id = (*it)->GetAccountId();
bool is_owner = (account_id == owner);
const bool is_public_account =
((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
const AuthType initial_auth_type =
is_public_account ? EXPAND_THEN_USER_CLICK
: (ShouldForceOnlineSignIn(*it) ? ONLINE_SIGN_IN
: OFFLINE_PASSWORD);
user_auth_type_map_[account_id] = initial_auth_type;
auto user_dict = base::MakeUnique<base::DictionaryValue>();
const std::vector<std::string>* public_session_recommended_locales =
public_session_recommended_locales_.find(account_id) ==
public_session_recommended_locales_.end()
? &kEmptyRecommendedLocales
: &public_session_recommended_locales_[account_id];
FillUserDictionary(*it, is_owner, is_signin_to_add, initial_auth_type,
public_session_recommended_locales, user_dict.get());
bool signed_in = (*it)->is_logged_in();
// 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
bool can_remove_user =
((!single_user || is_enterprise_managed) && account_id.is_valid() &&
!is_owner && !is_public_account && !signed_in && !is_signin_to_add);
user_dict->SetBoolean(kKeyCanRemove, can_remove_user);
users_list.Append(std::move(user_dict));
}
handler_->LoadUsers(users_list, show_guest_);
}
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()));
}
}
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, ONLINE_SIGN_IN, base::string16());
}
}
// EasyUnlock stuff
void UserSelectionScreen::SetAuthType(const AccountId& account_id,
AuthType auth_type,
const base::string16& initial_value) {
if (GetAuthType(account_id) == FORCE_OFFLINE_PASSWORD)
return;
DCHECK(GetAuthType(account_id) != FORCE_OFFLINE_PASSWORD ||
auth_type == FORCE_OFFLINE_PASSWORD);
user_auth_type_map_[account_id] = auth_type;
view_->SetAuthType(account_id, auth_type, initial_value);
}
proximity_auth::ScreenlockBridge::LockHandler::AuthType
UserSelectionScreen::GetAuthType(const AccountId& account_id) const {
if (user_auth_type_map_.find(account_id) == user_auth_type_map_.end())
return 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) {
view_->ShowBannerMessage(message);
}
void UserSelectionScreen::ShowUserPodCustomIcon(
const AccountId& account_id,
const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
icon_options) {
std::unique_ptr<base::DictionaryValue> icon =
icon_options.ToDictionaryValue();
if (!icon || icon->empty())
return;
view_->ShowUserPodCustomIcon(account_id, *icon);
}
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);
UserContext user_context(account_id);
user_context.SetAuthFlow(UserContext::AUTH_FLOW_EASY_UNLOCK);
user_context.SetKey(Key(secret));
user_context.GetKey()->SetLabel(key_label);
login_display_delegate_->Login(user_context, SigninSpecifics());
}
void UserSelectionScreen::HardLockPod(const AccountId& account_id) {
view_->SetAuthType(account_id, 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);
}
void UserSelectionScreen::RecordClickOnLockIcon(const AccountId& account_id) {
EasyUnlockService* service = GetEasyUnlockServiceForUser(account_id);
if (!service)
return;
service->RecordClickOnLockIcon();
}
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