1 // Copyright 2013 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.
5 #include "chrome/browser/ui/webui/signin/user_manager_screen_handler.h"
8 #include "base/value_conversions.h"
9 #include "base/values.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/avatar_menu.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_info_cache.h"
14 #include "chrome/browser/profiles/profile_info_cache_observer.h"
15 #include "chrome/browser/profiles/profile_info_util.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/profiles/profile_window.h"
18 #include "chrome/browser/profiles/profiles_state.h"
19 #include "chrome/browser/signin/local_auth.h"
20 #include "chrome/browser/ui/browser_dialogs.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/singleton_tabs.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_view.h"
25 #include "content/public/browser/web_ui.h"
26 #include "google_apis/gaia/gaia_auth_fetcher.h"
27 #include "google_apis/gaia/gaia_constants.h"
28 #include "grit/browser_resources.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "third_party/skia/include/core/SkBitmap.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/webui/web_ui_util.h"
34 #include "ui/gfx/image/image_util.h"
36 #if defined(ENABLE_MANAGED_USERS)
37 #include "chrome/browser/managed_mode/managed_user_service.h"
41 // User dictionary keys.
42 const char kKeyUsername
[] = "username";
43 const char kKeyDisplayName
[]= "displayName";
44 const char kKeyEmailAddress
[] = "emailAddress";
45 const char kKeyProfilePath
[] = "profilePath";
46 const char kKeyPublicAccount
[] = "publicAccount";
47 const char kKeyLocallyManagedUser
[] = "locallyManagedUser";
48 const char kKeySignedIn
[] = "signedIn";
49 const char kKeyCanRemove
[] = "canRemove";
50 const char kKeyIsOwner
[] = "isOwner";
51 const char kKeyIsDesktop
[] = "isDesktopUser";
52 const char kKeyAvatarUrl
[] = "userImage";
53 const char kKeyNeedsSignin
[] = "needsSignin";
55 // JS API callback names.
56 const char kJsApiUserManagerInitialize
[] = "userManagerInitialize";
57 const char kJsApiUserManagerAddUser
[] = "addUser";
58 const char kJsApiUserManagerAuthLaunchUser
[] = "authenticatedLaunchUser";
59 const char kJsApiUserManagerLaunchGuest
[] = "launchGuest";
60 const char kJsApiUserManagerLaunchUser
[] = "launchUser";
61 const char kJsApiUserManagerRemoveUser
[] = "removeUser";
63 const size_t kAvatarIconSize
= 180;
65 void HandleAndDoNothing(const base::ListValue
* args
) {
68 // This callback is run if the only profile has been deleted, and a new
69 // profile has been created to replace it.
70 void OpenNewWindowForProfile(
71 chrome::HostDesktopType desktop_type
,
73 Profile::CreateStatus status
) {
74 if (status
!= Profile::CREATE_STATUS_INITIALIZED
)
76 profiles::FindOrCreateNewWindowForProfile(
78 chrome::startup::IS_PROCESS_STARTUP
,
79 chrome::startup::IS_FIRST_RUN
,
84 std::string
GetAvatarImageAtIndex(
85 size_t index
, const ProfileInfoCache
& info_cache
) {
86 bool is_gaia_picture
=
87 info_cache
.IsUsingGAIAPictureOfProfileAtIndex(index
) &&
88 info_cache
.GetGAIAPictureOfProfileAtIndex(index
);
90 gfx::Image icon
= profiles::GetSizedAvatarIconWithBorder(
91 info_cache
.GetAvatarIconOfProfileAtIndex(index
),
92 is_gaia_picture
, kAvatarIconSize
, kAvatarIconSize
);
93 return webui::GetBitmapDataUrl(icon
.AsBitmap());
96 size_t GetIndexOfProfileWithEmailAndName(const ProfileInfoCache
& info_cache
,
97 const base::string16
& email
,
98 const base::string16
& name
) {
99 for (size_t i
= 0; i
< info_cache
.GetNumberOfProfiles(); ++i
) {
100 if (info_cache
.GetUserNameOfProfileAtIndex(i
) == email
&&
101 info_cache
.GetNameOfProfileAtIndex(i
) == name
) {
105 return std::string::npos
;
110 // ProfileUpdateObserver ------------------------------------------------------
112 class UserManagerScreenHandler::ProfileUpdateObserver
113 : public ProfileInfoCacheObserver
{
115 ProfileUpdateObserver(
116 ProfileManager
* profile_manager
, UserManagerScreenHandler
* handler
)
117 : profile_manager_(profile_manager
),
118 user_manager_handler_(handler
) {
119 DCHECK(profile_manager_
);
120 DCHECK(user_manager_handler_
);
121 profile_manager_
->GetProfileInfoCache().AddObserver(this);
124 virtual ~ProfileUpdateObserver() {
125 DCHECK(profile_manager_
);
126 profile_manager_
->GetProfileInfoCache().RemoveObserver(this);
130 // ProfileInfoCacheObserver implementation:
131 // If any change has been made to a profile, propagate it to all the
132 // visible user manager screens.
133 virtual void OnProfileAdded(const base::FilePath
& profile_path
) OVERRIDE
{
134 user_manager_handler_
->SendUserList();
137 virtual void OnProfileWasRemoved(
138 const base::FilePath
& profile_path
,
139 const base::string16
& profile_name
) OVERRIDE
{
140 // TODO(noms): Change 'SendUserList' to 'removeUser' JS-call when
141 // UserManager is able to find pod belonging to removed user.
142 user_manager_handler_
->SendUserList();
145 virtual void OnProfileNameChanged(
146 const base::FilePath
& profile_path
,
147 const base::string16
& old_profile_name
) OVERRIDE
{
148 user_manager_handler_
->SendUserList();
151 virtual void OnProfileAvatarChanged(
152 const base::FilePath
& profile_path
) OVERRIDE
{
153 user_manager_handler_
->SendUserList();
156 virtual void OnProfileSigninRequiredChanged(
157 const base::FilePath
& profile_path
) OVERRIDE
{
158 user_manager_handler_
->SendUserList();
161 ProfileManager
* profile_manager_
;
163 UserManagerScreenHandler
* user_manager_handler_
; // Weak; owns us.
165 DISALLOW_COPY_AND_ASSIGN(ProfileUpdateObserver
);
168 // UserManagerScreenHandler ---------------------------------------------------
170 UserManagerScreenHandler::UserManagerScreenHandler()
171 : desktop_type_(chrome::GetActiveDesktop()) {
172 profileInfoCacheObserver_
.reset(
173 new UserManagerScreenHandler::ProfileUpdateObserver(
174 g_browser_process
->profile_manager(), this));
177 UserManagerScreenHandler::~UserManagerScreenHandler() {
180 void UserManagerScreenHandler::HandleInitialize(const base::ListValue
* args
) {
182 web_ui()->CallJavascriptFunction("cr.ui.Oobe.showUserManagerScreen");
183 desktop_type_
= chrome::GetHostDesktopTypeForNativeView(
184 web_ui()->GetWebContents()->GetView()->GetNativeView());
187 void UserManagerScreenHandler::HandleAddUser(const base::ListValue
* args
) {
188 profiles::CreateAndSwitchToNewProfile(desktop_type_
,
189 base::Bind(&chrome::HideUserManager
));
192 void UserManagerScreenHandler::HandleAuthenticatedLaunchUser(
193 const base::ListValue
* args
) {
194 base::string16 email_address
;
195 if (!args
->GetString(0, &email_address
))
198 base::string16 display_name
;
199 if (!args
->GetString(1, &display_name
))
202 std::string password
;
203 if (!args
->GetString(2, &password
))
206 ProfileInfoCache
& info_cache
=
207 g_browser_process
->profile_manager()->GetProfileInfoCache();
208 size_t profile_index
= GetIndexOfProfileWithEmailAndName(
209 info_cache
, email_address
, display_name
);
210 if (profile_index
>= info_cache
.GetNumberOfProfiles()) {
215 authenticating_profile_index_
= profile_index
;
216 if (!chrome::ValidateLocalAuthCredentials(profile_index
, password
)) {
217 // Make a second attempt via an on-line authentication call. This handles
218 // profiles that are missing sign-in credentials and also cases where the
219 // password has been changed externally.
220 client_login_
.reset(new GaiaAuthFetcher(
222 GaiaConstants::kChromeSource
,
223 web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext()));
224 std::string email_string
;
225 args
->GetString(0, &email_string
);
226 client_login_
->StartClientLogin(
229 GaiaConstants::kSyncService
,
232 GaiaAuthFetcher::HostedAccountsAllowed
);
233 password_attempt_
= password
;
237 ReportAuthenticationResult(true);
240 void UserManagerScreenHandler::HandleRemoveUser(const base::ListValue
* args
) {
242 const base::Value
* profile_path_value
;
243 if (!args
->Get(0, &profile_path_value
))
246 base::FilePath profile_path
;
247 if (!base::GetValueAsFilePath(*profile_path_value
, &profile_path
))
250 // This handler could have been called in managed mode, for example because
251 // the user fiddled with the web inspector. Silently return in this case.
252 if (Profile::FromWebUI(web_ui())->IsManaged())
255 if (!profiles::IsMultipleProfilesEnabled())
258 g_browser_process
->profile_manager()->ScheduleProfileForDeletion(
260 base::Bind(&OpenNewWindowForProfile
, desktop_type_
));
263 void UserManagerScreenHandler::HandleLaunchGuest(const base::ListValue
* args
) {
264 profiles::SwitchToGuestProfile(desktop_type_
,
265 base::Bind(&chrome::HideUserManager
));
268 void UserManagerScreenHandler::HandleLaunchUser(const base::ListValue
* args
) {
269 base::string16 email_address
;
270 base::string16 display_name
;
272 if (!args
->GetString(0, &email_address
) ||
273 !args
->GetString(1, &display_name
)) {
278 ProfileInfoCache
& info_cache
=
279 g_browser_process
->profile_manager()->GetProfileInfoCache();
280 size_t profile_index
= GetIndexOfProfileWithEmailAndName(
281 info_cache
, email_address
, display_name
);
283 if (profile_index
>= info_cache
.GetNumberOfProfiles()) {
288 // It's possible that a user breaks into the user-manager page using the
289 // JavaScript Inspector and causes a "locked" profile to call this
290 // unauthenticated version of "launch" instead of the proper one. Thus,
291 // we have to validate in (secure) C++ code that it really is a profile
292 // not needing authentication. If it is, just ignore the "launch" request.
293 if (info_cache
.ProfileIsSigninRequiredAtIndex(profile_index
))
296 base::FilePath path
= info_cache
.GetPathOfProfileAtIndex(profile_index
);
297 profiles::SwitchToProfile(
298 path
, desktop_type_
, true, base::Bind(&chrome::HideUserManager
));
301 void UserManagerScreenHandler::OnClientLoginSuccess(
302 const ClientLoginResult
& result
) {
303 chrome::SetLocalAuthCredentials(authenticating_profile_index_
,
305 ReportAuthenticationResult(true);
308 void UserManagerScreenHandler::OnClientLoginFailure(
309 const GoogleServiceAuthError
& error
) {
310 ReportAuthenticationResult(false);
313 void UserManagerScreenHandler::RegisterMessages() {
314 web_ui()->RegisterMessageCallback(kJsApiUserManagerInitialize
,
315 base::Bind(&UserManagerScreenHandler::HandleInitialize
,
316 base::Unretained(this)));
317 web_ui()->RegisterMessageCallback(kJsApiUserManagerAddUser
,
318 base::Bind(&UserManagerScreenHandler::HandleAddUser
,
319 base::Unretained(this)));
320 web_ui()->RegisterMessageCallback(kJsApiUserManagerAuthLaunchUser
,
321 base::Bind(&UserManagerScreenHandler::HandleAuthenticatedLaunchUser
,
322 base::Unretained(this)));
323 web_ui()->RegisterMessageCallback(kJsApiUserManagerLaunchGuest
,
324 base::Bind(&UserManagerScreenHandler::HandleLaunchGuest
,
325 base::Unretained(this)));
326 web_ui()->RegisterMessageCallback(kJsApiUserManagerLaunchUser
,
327 base::Bind(&UserManagerScreenHandler::HandleLaunchUser
,
328 base::Unretained(this)));
329 web_ui()->RegisterMessageCallback(kJsApiUserManagerRemoveUser
,
330 base::Bind(&UserManagerScreenHandler::HandleRemoveUser
,
331 base::Unretained(this)));
333 const content::WebUI::MessageCallback
& kDoNothingCallback
=
334 base::Bind(&HandleAndDoNothing
);
336 // Unused callbacks from screen_account_picker.js
337 web_ui()->RegisterMessageCallback("accountPickerReady", kDoNothingCallback
);
338 web_ui()->RegisterMessageCallback("loginUIStateChanged", kDoNothingCallback
);
339 web_ui()->RegisterMessageCallback("hideCaptivePortal", kDoNothingCallback
);
340 // Unused callbacks from display_manager.js
341 web_ui()->RegisterMessageCallback("showAddUser", kDoNothingCallback
);
342 web_ui()->RegisterMessageCallback("loadWallpaper", kDoNothingCallback
);
343 web_ui()->RegisterMessageCallback("updateCurrentScreen", kDoNothingCallback
);
344 web_ui()->RegisterMessageCallback("loginVisible", kDoNothingCallback
);
345 // Unused callbacks from user_pod_row.js
346 web_ui()->RegisterMessageCallback("focusPod", kDoNothingCallback
);
349 void UserManagerScreenHandler::GetLocalizedValues(
350 base::DictionaryValue
* localized_strings
) {
352 localized_strings
->SetString("signedIn",
353 l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_ACTIVE_USER
));
354 localized_strings
->SetString("signinButton",
355 l10n_util::GetStringUTF16(IDS_LOGIN_BUTTON
));
356 localized_strings
->SetString("addUser",
357 l10n_util::GetStringUTF16(IDS_ADD_USER_BUTTON
));
358 localized_strings
->SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL
));
359 localized_strings
->SetString("browseAsGuest",
360 l10n_util::GetStringUTF16(IDS_GO_INCOGNITO_BUTTON
));
361 localized_strings
->SetString("signOutUser",
362 l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT
));
364 // For AccountPickerScreen.
365 localized_strings
->SetString("screenType", "login-add-user");
366 localized_strings
->SetString("highlightStrength", "normal");
367 localized_strings
->SetString("title",
368 l10n_util::GetStringUTF16(IDS_USER_MANAGER_SCREEN_TITLE
));
369 localized_strings
->SetString("passwordHint",
370 l10n_util::GetStringUTF16(IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT
));
371 localized_strings
->SetString("podMenuButtonAccessibleName",
372 l10n_util::GetStringUTF16(IDS_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME
));
373 localized_strings
->SetString("podMenuRemoveItemAccessibleName",
374 l10n_util::GetStringUTF16(
375 IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME
));
376 localized_strings
->SetString("removeUser",
377 l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON
));
378 localized_strings
->SetString("passwordFieldAccessibleName",
379 l10n_util::GetStringUTF16(IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME
));
380 localized_strings
->SetString("bootIntoWallpaper", "off");
382 // For AccountPickerScreen, the remove user warning overlay.
383 localized_strings
->SetString("removeUserWarningButtonTitle",
384 l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON
));
385 localized_strings
->SetString("removeUserWarningText",
386 l10n_util::GetStringUTF16(
387 IDS_LOGIN_POD_USER_REMOVE_WARNING
));
389 // Strings needed for the user_pod_template public account div, but not ever
390 // actually displayed for desktop users.
391 localized_strings
->SetString("publicAccountReminder", base::string16());
392 localized_strings
->SetString("publicAccountEnter", base::string16());
393 localized_strings
->SetString("publicAccountEnterAccessibleName",
395 localized_strings
->SetString("multiple-signin-banner-text",
399 void UserManagerScreenHandler::SendUserList() {
400 base::ListValue users_list
;
401 base::FilePath active_profile_path
=
402 web_ui()->GetWebContents()->GetBrowserContext()->GetPath();
403 const ProfileInfoCache
& info_cache
=
404 g_browser_process
->profile_manager()->GetProfileInfoCache();
406 // If the active user is a managed user, then they may not perform
407 // certain actions (i.e. delete another user).
408 bool active_user_is_managed
= Profile::FromWebUI(web_ui())->IsManaged();
409 for (size_t i
= 0; i
< info_cache
.GetNumberOfProfiles(); ++i
) {
410 base::DictionaryValue
* profile_value
= new base::DictionaryValue();
412 base::FilePath profile_path
= info_cache
.GetPathOfProfileAtIndex(i
);
413 bool is_active_user
= (profile_path
== active_profile_path
);
415 profile_value
->SetString(
416 kKeyUsername
, info_cache
.GetUserNameOfProfileAtIndex(i
));
417 profile_value
->SetString(
418 kKeyEmailAddress
, info_cache
.GetUserNameOfProfileAtIndex(i
));
419 profile_value
->SetString(
420 kKeyDisplayName
, info_cache
.GetNameOfProfileAtIndex(i
));
421 profile_value
->SetString(kKeyProfilePath
, profile_path
.MaybeAsASCII());
422 profile_value
->SetBoolean(kKeyPublicAccount
, false);
423 profile_value
->SetBoolean(kKeyLocallyManagedUser
, false);
424 profile_value
->SetBoolean(kKeySignedIn
, is_active_user
);
425 profile_value
->SetBoolean(
426 kKeyNeedsSignin
, info_cache
.ProfileIsSigninRequiredAtIndex(i
));
427 profile_value
->SetBoolean(kKeyIsOwner
, false);
428 profile_value
->SetBoolean(kKeyCanRemove
, !active_user_is_managed
);
429 profile_value
->SetBoolean(kKeyIsDesktop
, true);
430 profile_value
->SetString(
431 kKeyAvatarUrl
, GetAvatarImageAtIndex(i
, info_cache
));
433 // The row of user pods should display the active user first.
435 users_list
.Insert(0, profile_value
);
437 users_list
.Append(profile_value
);
440 web_ui()->CallJavascriptFunction("login.AccountPickerScreen.loadUsers",
441 users_list
, base::FundamentalValue(false), base::FundamentalValue(true));
444 void UserManagerScreenHandler::ReportAuthenticationResult(bool success
) {
446 ProfileInfoCache
& info_cache
=
447 g_browser_process
->profile_manager()->GetProfileInfoCache();
448 info_cache
.SetProfileSigninRequiredAtIndex(
449 authenticating_profile_index_
, false);
450 base::FilePath path
= info_cache
.GetPathOfProfileAtIndex(
451 authenticating_profile_index_
);
452 profiles::SwitchToProfile(path
, desktop_type_
, true,
453 base::Bind(&chrome::HideUserManager
));
455 web_ui()->CallJavascriptFunction(
456 "cr.ui.Oobe.showSignInError",
457 base::FundamentalValue(0),
459 l10n_util::GetStringUTF8(IDS_LOGIN_ERROR_AUTHENTICATING
)),
460 base::StringValue(""),
461 base::FundamentalValue(0));
464 password_attempt_
.clear();