user_selection_screen.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
6
7#include <vector>
8
9#include "ash/shell.h"
10#include "base/location.h"
11#include "base/logging.h"
12#include "base/prefs/pref_service.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/browser_process_platform_part.h"
16#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
17#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
18#include "chrome/browser/chromeos/login/users/user_manager.h"
19#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
20#include "chrome/browser/signin/screenlock_bridge.h"
21#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
22#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
23#include "chrome/common/pref_names.h"
24#include "components/user_manager/user_type.h"
25#include "ui/wm/core/user_activity_detector.h"
26
27namespace chromeos {
28
29namespace {
30
31// User dictionary keys.
32const char kKeyUsername[] = "username";
33const char kKeyDisplayName[] = "displayName";
34const char kKeyEmailAddress[] = "emailAddress";
35const char kKeyEnterpriseDomain[] = "enterpriseDomain";
36const char kKeyPublicAccount[] = "publicAccount";
37const char kKeySupervisedUser[] = "supervisedUser";
38const char kKeySignedIn[] = "signedIn";
39const char kKeyCanRemove[] = "canRemove";
40const char kKeyIsOwner[] = "isOwner";
41const char kKeyInitialAuthType[] = "initialAuthType";
42const char kKeyMultiProfilesAllowed[] = "isMultiProfilesAllowed";
43const char kKeyMultiProfilesPolicy[] = "multiProfilesPolicy";
44const char kKeyInitialLocales[] = "initialLocales";
45const char kKeyInitialLocale[] = "initialLocale";
46const char kKeyInitialMultipleRecommendedLocales[] =
47    "initialMultipleRecommendedLocales";
48const char kKeyInitialKeyboardLayout[] = "initialKeyboardLayout";
49
50// Max number of users to show.
51// Please keep synced with one in signin_userlist_unittest.cc.
52const size_t kMaxUsers = 18;
53
54const int kPasswordClearTimeoutSec = 60;
55
56void AddPublicSessionDetailsToUserDictionaryEntry(
57    base::DictionaryValue* user_dict,
58    const std::vector<std::string>* public_session_recommended_locales) {
59  policy::BrowserPolicyConnectorChromeOS* policy_connector =
60      g_browser_process->platform_part()->browser_policy_connector_chromeos();
61
62  if (policy_connector->IsEnterpriseManaged()) {
63    user_dict->SetString(kKeyEnterpriseDomain,
64                         policy_connector->GetEnterpriseDomain());
65  }
66
67  std::vector<std::string> kEmptyRecommendedLocales;
68  const std::vector<std::string>* recommended_locales =
69      public_session_recommended_locales ?
70          public_session_recommended_locales : &kEmptyRecommendedLocales;
71
72  // Set |kKeyInitialLocales| to the list of available locales. This list
73  // consists of the recommended locales, followed by all others.
74  user_dict->Set(
75      kKeyInitialLocales,
76      GetUILanguageList(recommended_locales, std::string()).release());
77
78  // Set |kKeyInitialLocale| to the initially selected locale. If the list of
79  // recommended locales is not empty, select its first entry. Otherwise,
80  // select the current UI locale.
81  user_dict->SetString(kKeyInitialLocale,
82                       recommended_locales->empty() ?
83                           g_browser_process->GetApplicationLocale() :
84                           recommended_locales->front());
85
86  // Set |kKeyInitialMultipleRecommendedLocales| to indicate whether the list
87  // of recommended locales contains at least two entries. This is used to
88  // decide whether the public session pod expands to its basic form (for zero
89  // or one recommended locales) or the advanced form (two or more recommended
90  // locales).
91  user_dict->SetBoolean(kKeyInitialMultipleRecommendedLocales,
92                        recommended_locales->size() >= 2);
93
94  // Set |kKeyInitialKeyboardLayout| to the current keyboard layout. This
95  // value will be used temporarily only because the UI immediately requests a
96  // list of keyboard layouts suitable for the currently selected locale.
97  user_dict->Set(kKeyInitialKeyboardLayout,
98                 GetCurrentKeyboardLayout().release());
99}
100
101}  // namespace
102
103UserSelectionScreen::UserSelectionScreen() : handler_(NULL) {
104}
105
106UserSelectionScreen::~UserSelectionScreen() {
107  wm::UserActivityDetector* activity_detector =
108      ash::Shell::GetInstance()->user_activity_detector();
109  if (activity_detector->HasObserver(this))
110    activity_detector->RemoveObserver(this);
111}
112
113// static
114void UserSelectionScreen::FillUserDictionary(
115    user_manager::User* user,
116    bool is_owner,
117    bool is_signin_to_add,
118    ScreenlockBridge::LockHandler::AuthType auth_type,
119    const std::vector<std::string>* public_session_recommended_locales,
120    base::DictionaryValue* user_dict) {
121  const std::string& user_id = user->email();
122  const bool is_public_session =
123      user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
124  const bool is_supervised_user =
125      user->GetType() == user_manager::USER_TYPE_SUPERVISED;
126
127  user_dict->SetString(kKeyUsername, user_id);
128  user_dict->SetString(kKeyEmailAddress, user->display_email());
129  user_dict->SetString(kKeyDisplayName, user->GetDisplayName());
130  user_dict->SetBoolean(kKeyPublicAccount, is_public_session);
131  user_dict->SetBoolean(kKeySupervisedUser, is_supervised_user);
132  user_dict->SetInteger(kKeyInitialAuthType, auth_type);
133  user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
134  user_dict->SetBoolean(kKeyIsOwner, is_owner);
135
136  // Fill in multi-profiles related fields.
137  if (is_signin_to_add) {
138    MultiProfileUserController* multi_profile_user_controller =
139        UserManager::Get()->GetMultiProfileUserController();
140    MultiProfileUserController::UserAllowedInSessionReason isUserAllowedReason;
141    bool isUserAllowed = multi_profile_user_controller->IsUserAllowedInSession(
142        user_id, &isUserAllowedReason);
143    user_dict->SetBoolean(kKeyMultiProfilesAllowed, isUserAllowed);
144
145    std::string behavior;
146    switch (isUserAllowedReason) {
147      case MultiProfileUserController::NOT_ALLOWED_OWNER_AS_SECONDARY:
148        behavior = MultiProfileUserController::kBehaviorOwnerPrimaryOnly;
149        break;
150      default:
151        behavior = multi_profile_user_controller->GetCachedValue(user_id);
152    }
153    user_dict->SetString(kKeyMultiProfilesPolicy, behavior);
154  } else {
155    user_dict->SetBoolean(kKeyMultiProfilesAllowed, true);
156  }
157
158  if (is_public_session) {
159    AddPublicSessionDetailsToUserDictionaryEntry(
160        user_dict,
161        public_session_recommended_locales);
162  }
163}
164
165// static
166bool UserSelectionScreen::ShouldForceOnlineSignIn(
167    const user_manager::User* user) {
168  // Public sessions are always allowed to log in offline.
169  // Supervised user are allowed to log in offline if their OAuth token status
170  // is unknown or valid.
171  // For all other users, force online sign in if:
172  // * The flag to force online sign-in is set for the user.
173  // * The user's OAuth token is invalid.
174  // * The user's OAuth token status is unknown (except supervised users,
175  //   see above).
176  if (user->is_logged_in())
177    return false;
178
179  const user_manager::User::OAuthTokenStatus token_status =
180      user->oauth_token_status();
181  const bool is_supervised_user =
182      user->GetType() == user_manager::USER_TYPE_SUPERVISED;
183  const bool is_public_session =
184      user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
185
186  if (is_supervised_user &&
187      token_status == user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN) {
188    return false;
189  }
190
191  if (is_public_session)
192    return false;
193
194  return user->force_online_signin() ||
195         (token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID) ||
196         (token_status == user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
197}
198
199void UserSelectionScreen::SetHandler(LoginDisplayWebUIHandler* handler) {
200  handler_ = handler;
201}
202
203void UserSelectionScreen::Init(const user_manager::UserList& users,
204                               bool show_guest) {
205  users_ = users;
206  show_guest_ = show_guest;
207
208  wm::UserActivityDetector* activity_detector =
209      ash::Shell::GetInstance()->user_activity_detector();
210  if (!activity_detector->HasObserver(this))
211    activity_detector->AddObserver(this);
212}
213
214void UserSelectionScreen::OnBeforeUserRemoved(const std::string& username) {
215  for (user_manager::UserList::iterator it = users_.begin(); it != users_.end();
216       ++it) {
217    if ((*it)->email() == username) {
218      users_.erase(it);
219      break;
220    }
221  }
222}
223
224void UserSelectionScreen::OnUserRemoved(const std::string& username) {
225  if (!handler_)
226    return;
227
228  handler_->OnUserRemoved(username);
229}
230
231void UserSelectionScreen::OnUserImageChanged(const user_manager::User& user) {
232  if (!handler_)
233    return;
234  handler_->OnUserImageChanged(user);
235  // TODO(antrim) : updateUserImage(user.email())
236}
237
238const user_manager::UserList& UserSelectionScreen::GetUsers() const {
239  return users_;
240}
241
242void UserSelectionScreen::OnPasswordClearTimerExpired() {
243  if (handler_)
244    handler_->ClearUserPodPassword();
245}
246
247void UserSelectionScreen::OnUserActivity(const ui::Event* event) {
248  if (!password_clear_timer_.IsRunning()) {
249    password_clear_timer_.Start(
250        FROM_HERE,
251        base::TimeDelta::FromSeconds(kPasswordClearTimeoutSec),
252        this,
253        &UserSelectionScreen::OnPasswordClearTimerExpired);
254  }
255  password_clear_timer_.Reset();
256}
257
258// static
259const user_manager::UserList UserSelectionScreen::PrepareUserListForSending(
260    const user_manager::UserList& users,
261    std::string owner,
262    bool is_signin_to_add) {
263  user_manager::UserList users_to_send;
264  bool has_owner = owner.size() > 0;
265  size_t max_non_owner_users = has_owner ? kMaxUsers - 1 : kMaxUsers;
266  size_t non_owner_count = 0;
267
268  for (user_manager::UserList::const_iterator it = users.begin();
269       it != users.end();
270       ++it) {
271    const std::string& user_id = (*it)->email();
272    bool is_owner = (user_id == owner);
273    bool is_public_account =
274        ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
275
276    if ((is_public_account && !is_signin_to_add) || is_owner ||
277        (!is_public_account && non_owner_count < max_non_owner_users)) {
278
279      if (!is_owner)
280        ++non_owner_count;
281      if (is_owner && users_to_send.size() > kMaxUsers) {
282        // Owner is always in the list.
283        users_to_send.insert(users_to_send.begin() + (kMaxUsers - 1), *it);
284        while (users_to_send.size() > kMaxUsers)
285          users_to_send.erase(users_to_send.begin() + kMaxUsers);
286      } else if (users_to_send.size() < kMaxUsers) {
287        users_to_send.push_back(*it);
288      }
289    }
290  }
291  return users_to_send;
292}
293
294void UserSelectionScreen::SendUserList() {
295  base::ListValue users_list;
296  const user_manager::UserList& users = GetUsers();
297
298  // TODO(nkostylev): Move to a separate method in UserManager.
299  // http://crbug.com/230852
300  bool single_user = users.size() == 1;
301  bool is_signin_to_add = LoginDisplayHostImpl::default_host() &&
302                          UserManager::Get()->IsUserLoggedIn();
303  std::string owner;
304  chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
305
306  policy::BrowserPolicyConnectorChromeOS* connector =
307      g_browser_process->platform_part()->browser_policy_connector_chromeos();
308  bool is_enterprise_managed = connector->IsEnterpriseManaged();
309
310  const user_manager::UserList users_to_send =
311      PrepareUserListForSending(users, owner, is_signin_to_add);
312
313  user_auth_type_map_.clear();
314
315  const std::vector<std::string> kEmptyRecommendedLocales;
316  for (user_manager::UserList::const_iterator it = users_to_send.begin();
317       it != users_to_send.end();
318       ++it) {
319    const std::string& user_id = (*it)->email();
320    bool is_owner = (user_id == owner);
321    const bool is_public_account =
322        ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT);
323    const ScreenlockBridge::LockHandler::AuthType initial_auth_type =
324        is_public_account
325            ? ScreenlockBridge::LockHandler::EXPAND_THEN_USER_CLICK
326            : (ShouldForceOnlineSignIn(*it)
327                   ? ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
328                   : ScreenlockBridge::LockHandler::OFFLINE_PASSWORD);
329    user_auth_type_map_[user_id] = initial_auth_type;
330
331    base::DictionaryValue* user_dict = new base::DictionaryValue();
332    const std::vector<std::string>* public_session_recommended_locales =
333        public_session_recommended_locales_.find(user_id) ==
334            public_session_recommended_locales_.end() ?
335                &kEmptyRecommendedLocales :
336                &public_session_recommended_locales_[user_id];
337    FillUserDictionary(*it,
338                       is_owner,
339                       is_signin_to_add,
340                       initial_auth_type,
341                       public_session_recommended_locales,
342                       user_dict);
343    bool signed_in = (*it)->is_logged_in();
344    // Single user check here is necessary because owner info might not be
345    // available when running into login screen on first boot.
346    // See http://crosbug.com/12723
347    bool can_remove_user =
348        ((!single_user || is_enterprise_managed) && !user_id.empty() &&
349         !is_owner && !is_public_account && !signed_in && !is_signin_to_add);
350    user_dict->SetBoolean(kKeyCanRemove, can_remove_user);
351    users_list.Append(user_dict);
352  }
353
354  handler_->LoadUsers(users_list, show_guest_);
355}
356
357void UserSelectionScreen::HandleGetUsers() {
358  SendUserList();
359}
360
361void UserSelectionScreen::SetAuthType(
362    const std::string& username,
363    ScreenlockBridge::LockHandler::AuthType auth_type) {
364  user_auth_type_map_[username] = auth_type;
365}
366
367ScreenlockBridge::LockHandler::AuthType UserSelectionScreen::GetAuthType(
368    const std::string& username) const {
369  if (user_auth_type_map_.find(username) == user_auth_type_map_.end())
370    return ScreenlockBridge::LockHandler::OFFLINE_PASSWORD;
371  return user_auth_type_map_.find(username)->second;
372}
373
374}  // namespace chromeos
375