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