1 // Copyright (c) 2011 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/existing_user_controller.h"
6
7 #include "base/command_line.h"
8 #include "base/message_loop.h"
9 #include "base/stringprintf.h"
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chromeos/boot_times_loader.h"
15 #include "chrome/browser/chromeos/cros/cros_library.h"
16 #include "chrome/browser/chromeos/cros/cryptohome_library.h"
17 #include "chrome/browser/chromeos/cros/login_library.h"
18 #include "chrome/browser/chromeos/cros/network_library.h"
19 #include "chrome/browser/chromeos/customization_document.h"
20 #include "chrome/browser/chromeos/login/helper.h"
21 #include "chrome/browser/chromeos/login/login_display_host.h"
22 #include "chrome/browser/chromeos/login/views_login_display.h"
23 #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
24 #include "chrome/browser/chromeos/login/wizard_controller.h"
25 #include "chrome/browser/chromeos/status/status_area_view.h"
26 #include "chrome/browser/chromeos/user_cros_settings_provider.h"
27 #include "chrome/browser/google/google_util.h"
28 #include "chrome/browser/prefs/pref_service.h"
29 #include "chrome/browser/profiles/profile_manager.h"
30 #include "chrome/browser/ui/views/window.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/net/gaia/google_service_auth_error.h"
33 #include "chrome/common/pref_names.h"
34 #include "content/common/notification_service.h"
35 #include "content/common/notification_type.h"
36 #include "grit/generated_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "views/window/window.h"
39
40 namespace chromeos {
41
42 namespace {
43
44 // Url for setting up sync authentication.
45 const char kSettingsSyncLoginURL[] = "chrome://settings/personal";
46
47 // URL that will be opened on when user logs in first time on the device.
48 const char kGetStartedURLPattern[] =
49 "http://www.gstatic.com/chromebook/gettingstarted/index-%s.html";
50
51 // URL for account creation.
52 const char kCreateAccountURL[] =
53 "https://www.google.com/accounts/NewAccount?service=mail";
54
55 // Landing URL when launching Guest mode to fix captive portal.
56 const char kCaptivePortalLaunchURL[] = "http://www.google.com/";
57
58 } // namespace
59
60 // static
61 ExistingUserController* ExistingUserController::current_controller_ = NULL;
62
63 ////////////////////////////////////////////////////////////////////////////////
64 // ExistingUserController, public:
65
ExistingUserController(LoginDisplayHost * host)66 ExistingUserController::ExistingUserController(LoginDisplayHost* host)
67 : host_(host),
68 num_login_attempts_(0),
69 user_settings_(new UserCrosSettingsProvider),
70 method_factory_(this) {
71 DCHECK(current_controller_ == NULL);
72 current_controller_ = this;
73
74 login_display_ = host_->CreateLoginDisplay(this);
75
76 registrar_.Add(this,
77 NotificationType::LOGIN_USER_IMAGE_CHANGED,
78 NotificationService::AllSources());
79 }
80
Init(const UserVector & users)81 void ExistingUserController::Init(const UserVector& users) {
82 UserVector filtered_users;
83 if (UserCrosSettingsProvider::cached_show_users_on_signin()) {
84 for (size_t i = 0; i < users.size(); ++i)
85 // TODO(xiyuan): Clean user profile whose email is not in whitelist.
86 if (UserCrosSettingsProvider::cached_allow_new_user() ||
87 UserCrosSettingsProvider::IsEmailInCachedWhitelist(
88 users[i].email())) {
89 filtered_users.push_back(users[i]);
90 }
91 }
92
93 // If no user pods are visible, fallback to single new user pod which will
94 // have guest session link.
95 bool show_guest = UserCrosSettingsProvider::cached_allow_guest() &&
96 !filtered_users.empty();
97 bool show_new_user = true;
98 login_display_->set_parent_window(GetNativeWindow());
99 login_display_->Init(filtered_users, show_guest, show_new_user);
100
101 LoginUtils::Get()->PrewarmAuthentication();
102 if (CrosLibrary::Get()->EnsureLoaded()) {
103 CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady();
104 CrosLibrary::Get()->GetCryptohomeLibrary()->
105 AsyncDoAutomaticFreeDiskSpaceControl(NULL);
106 }
107 }
108
109 ////////////////////////////////////////////////////////////////////////////////
110 // ExistingUserController, NotificationObserver implementation:
111 //
112
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)113 void ExistingUserController::Observe(NotificationType type,
114 const NotificationSource& source,
115 const NotificationDetails& details) {
116 if (type != NotificationType::LOGIN_USER_IMAGE_CHANGED)
117 return;
118
119 UserManager::User* user = Details<UserManager::User>(details).ptr();
120 login_display_->OnUserImageChanged(user);
121 }
122
123 ////////////////////////////////////////////////////////////////////////////////
124 // ExistingUserController, private:
125
~ExistingUserController()126 ExistingUserController::~ExistingUserController() {
127 if (current_controller_ == this) {
128 current_controller_ = NULL;
129 } else {
130 NOTREACHED() << "More than one controller are alive.";
131 }
132 DCHECK(login_display_ != NULL);
133 login_display_->Destroy();
134 login_display_ = NULL;
135 }
136
137 ////////////////////////////////////////////////////////////////////////////////
138 // ExistingUserController, LoginDisplay::Delegate implementation:
139 //
140
CreateAccount()141 void ExistingUserController::CreateAccount() {
142 guest_mode_url_ =
143 google_util::AppendGoogleLocaleParam(GURL(kCreateAccountURL));
144 LoginAsGuest();
145 }
146
GetConnectedNetworkName()147 string16 ExistingUserController::GetConnectedNetworkName() {
148 return GetCurrentNetworkName(CrosLibrary::Get()->GetNetworkLibrary());
149 }
150
FixCaptivePortal()151 void ExistingUserController::FixCaptivePortal() {
152 guest_mode_url_ = GURL(kCaptivePortalLaunchURL);
153 LoginAsGuest();
154 }
155
Login(const std::string & username,const std::string & password)156 void ExistingUserController::Login(const std::string& username,
157 const std::string& password) {
158 if (username.empty() || password.empty())
159 return;
160 SetStatusAreaEnabled(false);
161 // Disable clicking on other windows.
162 login_display_->SetUIEnabled(false);
163
164 BootTimesLoader::Get()->RecordLoginAttempted();
165
166 if (last_login_attempt_username_ != username) {
167 last_login_attempt_username_ = username;
168 num_login_attempts_ = 0;
169 }
170 num_login_attempts_++;
171
172 // Use the same LoginPerformer for subsequent login as it has state
173 // such as CAPTCHA challenge token & corresponding user input.
174 if (!login_performer_.get() || num_login_attempts_ <= 1) {
175 LoginPerformer::Delegate* delegate = this;
176 if (login_performer_delegate_.get())
177 delegate = login_performer_delegate_.get();
178 // Only one instance of LoginPerformer should exist at a time.
179 login_performer_.reset(NULL);
180 login_performer_.reset(new LoginPerformer(delegate));
181 }
182 login_performer_->Login(username, password);
183 WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
184 l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNING_IN).c_str(),
185 false, true);
186 }
187
LoginAsGuest()188 void ExistingUserController::LoginAsGuest() {
189 SetStatusAreaEnabled(false);
190 // Disable clicking on other windows.
191 login_display_->SetUIEnabled(false);
192
193 // Check allow_guest in case this call is fired from key accelerator.
194 // Must not proceed without signature verification.
195 bool trusted_setting_available = user_settings_->RequestTrustedAllowGuest(
196 method_factory_.NewRunnableMethod(
197 &ExistingUserController::LoginAsGuest));
198 if (!trusted_setting_available) {
199 // Value of AllowGuest setting is still not verified.
200 // Another attempt will be invoked again after verification completion.
201 return;
202 }
203 if (!UserCrosSettingsProvider::cached_allow_guest()) {
204 // Disallowed.
205 return;
206 }
207
208 // Only one instance of LoginPerformer should exist at a time.
209 login_performer_.reset(NULL);
210 login_performer_.reset(new LoginPerformer(this));
211 login_performer_->LoginOffTheRecord();
212 WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
213 l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_OFFRECORD).c_str(),
214 false, true);
215 }
216
OnUserSelected(const std::string & username)217 void ExistingUserController::OnUserSelected(const std::string& username) {
218 login_performer_.reset(NULL);
219 num_login_attempts_ = 0;
220 }
221
OnStartEnterpriseEnrollment()222 void ExistingUserController::OnStartEnterpriseEnrollment() {
223 CommandLine* command_line = CommandLine::ForCurrentProcess();
224 if (command_line->HasSwitch(switches::kEnableDevicePolicy)) {
225 ownership_checker_.reset(new OwnershipStatusChecker(NewCallback(
226 this, &ExistingUserController::OnEnrollmentOwnershipCheckCompleted)));
227 }
228 }
229
OnEnrollmentOwnershipCheckCompleted(OwnershipService::Status status)230 void ExistingUserController::OnEnrollmentOwnershipCheckCompleted(
231 OwnershipService::Status status) {
232 if (status == OwnershipService::OWNERSHIP_NONE) {
233 host_->StartWizard(WizardController::kEnterpriseEnrollmentScreenName,
234 GURL());
235 login_display_->OnFadeOut();
236 }
237 ownership_checker_.reset();
238 }
239
240 ////////////////////////////////////////////////////////////////////////////////
241 // ExistingUserController, LoginPerformer::Delegate implementation:
242 //
243
OnLoginFailure(const LoginFailure & failure)244 void ExistingUserController::OnLoginFailure(const LoginFailure& failure) {
245 guest_mode_url_ = GURL::EmptyGURL();
246 std::string error = failure.GetErrorString();
247
248 // Check networking after trying to login in case user is
249 // cached locally or the local admin account.
250 bool is_known_user =
251 UserManager::Get()->IsKnownUser(last_login_attempt_username_);
252 NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary();
253 if (!network || !CrosLibrary::Get()->EnsureLoaded()) {
254 ShowError(IDS_LOGIN_ERROR_NO_NETWORK_LIBRARY, error);
255 } else if (!network->Connected()) {
256 if (is_known_user)
257 ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
258 else
259 ShowError(IDS_LOGIN_ERROR_OFFLINE_FAILED_NETWORK_NOT_CONNECTED, error);
260 } else {
261 if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
262 failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) {
263 if (!failure.error().captcha().image_url.is_empty()) {
264 CaptchaView* view =
265 new CaptchaView(failure.error().captcha().image_url, false);
266 view->Init();
267 view->set_delegate(this);
268 views::Window* window = browser::CreateViewsWindow(
269 GetNativeWindow(), gfx::Rect(), view);
270 window->SetIsAlwaysOnTop(true);
271 window->Show();
272 } else {
273 LOG(WARNING) << "No captcha image url was found?";
274 ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
275 }
276 } else if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
277 failure.error().state() ==
278 GoogleServiceAuthError::HOSTED_NOT_ALLOWED) {
279 ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED, error);
280 } else if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
281 failure.error().state() ==
282 GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
283 // SERVICE_UNAVAILABLE is generated in 2 cases:
284 // 1. ClientLogin returns ServiceUnavailable code.
285 // 2. Internet connectivity may be behind the captive portal.
286 // Suggesting user to try sign in to a portal in Guest mode.
287 if (UserCrosSettingsProvider::cached_allow_guest())
288 ShowError(IDS_LOGIN_ERROR_CAPTIVE_PORTAL, error);
289 else
290 ShowError(IDS_LOGIN_ERROR_CAPTIVE_PORTAL_NO_GUEST_MODE, error);
291 } else {
292 if (!is_known_user)
293 ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error);
294 else
295 ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
296 }
297 }
298
299 // Reenable clicking on other windows and status area.
300 login_display_->SetUIEnabled(true);
301 SetStatusAreaEnabled(true);
302 }
303
OnLoginSuccess(const std::string & username,const std::string & password,const GaiaAuthConsumer::ClientLoginResult & credentials,bool pending_requests)304 void ExistingUserController::OnLoginSuccess(
305 const std::string& username,
306 const std::string& password,
307 const GaiaAuthConsumer::ClientLoginResult& credentials,
308 bool pending_requests) {
309 bool known_user = UserManager::Get()->IsKnownUser(username);
310 bool login_only =
311 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
312 switches::kLoginScreen) == WizardController::kLoginScreenName;
313 ready_for_browser_launch_ = known_user || login_only;
314
315 two_factor_credentials_ = credentials.two_factor;
316
317 // LoginPerformer instance will delete itself once online auth result is OK.
318 // In case of failure it'll bring up ScreenLock and ask for
319 // correct password/display error message.
320 // Even in case when following online,offline protocol and returning
321 // requests_pending = false, let LoginPerformer delete itself.
322 login_performer_->set_delegate(NULL);
323 LoginPerformer* performer = login_performer_.release();
324 performer = NULL;
325
326 // Will call OnProfilePrepared() in the end.
327 LoginUtils::Get()->PrepareProfile(username,
328 password,
329 credentials,
330 pending_requests,
331 this);
332
333 }
334
OnProfilePrepared(Profile * profile)335 void ExistingUserController::OnProfilePrepared(Profile* profile) {
336 // TODO(nkostylev): May add login UI implementation callback call.
337 if (!ready_for_browser_launch_) {
338 PrefService* prefs = g_browser_process->local_state();
339 const std::string current_locale =
340 StringToLowerASCII(prefs->GetString(prefs::kApplicationLocale));
341 std::string start_url =
342 base::StringPrintf(kGetStartedURLPattern, current_locale.c_str());
343 CommandLine::ForCurrentProcess()->AppendArg(start_url);
344
345 ServicesCustomizationDocument* customization =
346 ServicesCustomizationDocument::GetInstance();
347 if (!ServicesCustomizationDocument::WasApplied() &&
348 customization->IsReady()) {
349 std::string locale = g_browser_process->GetApplicationLocale();
350 std::string initial_start_page =
351 customization->GetInitialStartPage(locale);
352 if (!initial_start_page.empty())
353 CommandLine::ForCurrentProcess()->AppendArg(initial_start_page);
354 customization->ApplyCustomization();
355 }
356
357 if (two_factor_credentials_) {
358 // If we have a two factor error and and this is a new user,
359 // load the personal settings page.
360 // TODO(stevenjb): direct the user to a lightweight sync login page.
361 CommandLine::ForCurrentProcess()->AppendArg(kSettingsSyncLoginURL);
362 }
363
364 ActivateWizard(WizardController::IsDeviceRegistered() ?
365 WizardController::kUserImageScreenName :
366 WizardController::kRegistrationScreenName);
367 } else {
368 LoginUtils::DoBrowserLaunch(profile);
369 // Delay deletion as we're on the stack.
370 host_->OnSessionStart();
371 }
372 login_display_->OnFadeOut();
373 }
374
OnOffTheRecordLoginSuccess()375 void ExistingUserController::OnOffTheRecordLoginSuccess() {
376 if (WizardController::IsDeviceRegistered()) {
377 LoginUtils::Get()->CompleteOffTheRecordLogin(guest_mode_url_);
378 } else {
379 // Postpone CompleteOffTheRecordLogin until registration completion.
380 ActivateWizard(WizardController::kRegistrationScreenName);
381 }
382 }
383
OnPasswordChangeDetected(const GaiaAuthConsumer::ClientLoginResult & credentials)384 void ExistingUserController::OnPasswordChangeDetected(
385 const GaiaAuthConsumer::ClientLoginResult& credentials) {
386 // Must not proceed without signature verification.
387 bool trusted_setting_available = user_settings_->RequestTrustedOwner(
388 method_factory_.NewRunnableMethod(
389 &ExistingUserController::OnPasswordChangeDetected,
390 credentials));
391 if (!trusted_setting_available) {
392 // Value of owner email is still not verified.
393 // Another attempt will be invoked after verification completion.
394 return;
395 }
396
397 // Passing 'false' here enables "full sync" mode in the dialog,
398 // which disables the requirement for the old owner password,
399 // allowing us to recover from a lost owner password/homedir.
400 // TODO(gspencer): We shouldn't have to erase stateful data when
401 // doing this. See http://crosbug.com/9115 http://crosbug.com/7792
402 PasswordChangedView* view = new PasswordChangedView(this, false);
403 views::Window* window = browser::CreateViewsWindow(GetNativeWindow(),
404 gfx::Rect(),
405 view);
406 window->SetIsAlwaysOnTop(true);
407 window->Show();
408 }
409
WhiteListCheckFailed(const std::string & email)410 void ExistingUserController::WhiteListCheckFailed(const std::string& email) {
411 ShowError(IDS_LOGIN_ERROR_WHITELIST, email);
412
413 // Reenable clicking on other windows and status area.
414 login_display_->SetUIEnabled(true);
415 SetStatusAreaEnabled(true);
416 }
417
418 ////////////////////////////////////////////////////////////////////////////////
419 // ExistingUserController, CaptchaView::Delegate implementation:
420 //
421
OnCaptchaEntered(const std::string & captcha)422 void ExistingUserController::OnCaptchaEntered(const std::string& captcha) {
423 login_performer_->set_captcha(captcha);
424 }
425
426 ////////////////////////////////////////////////////////////////////////////////
427 // ExistingUserController, PasswordChangedView::Delegate implementation:
428 //
429
RecoverEncryptedData(const std::string & old_password)430 void ExistingUserController::RecoverEncryptedData(
431 const std::string& old_password) {
432 // LoginPerformer instance has state of the user so it should exist.
433 if (login_performer_.get())
434 login_performer_->RecoverEncryptedData(old_password);
435 }
436
ResyncEncryptedData()437 void ExistingUserController::ResyncEncryptedData() {
438 // LoginPerformer instance has state of the user so it should exist.
439 if (login_performer_.get())
440 login_performer_->ResyncEncryptedData();
441 }
442
443 ////////////////////////////////////////////////////////////////////////////////
444 // ExistingUserController, private:
445
ActivateWizard(const std::string & screen_name)446 void ExistingUserController::ActivateWizard(const std::string& screen_name) {
447 GURL start_url;
448 if (chromeos::UserManager::Get()->IsLoggedInAsGuest())
449 start_url = guest_mode_url_;
450 host_->StartWizard(screen_name, start_url);
451 }
452
GetNativeWindow() const453 gfx::NativeWindow ExistingUserController::GetNativeWindow() const {
454 return host_->GetNativeWindow();
455 }
456
SetStatusAreaEnabled(bool enable)457 void ExistingUserController::SetStatusAreaEnabled(bool enable) {
458 host_->SetStatusAreaEnabled(enable);
459 }
460
ShowError(int error_id,const std::string & details)461 void ExistingUserController::ShowError(int error_id,
462 const std::string& details) {
463 // TODO(dpolukhin): show detailed error info. |details| string contains
464 // low level error info that is not localized and even is not user friendly.
465 // For now just ignore it because error_text contains all required information
466 // for end users, developers can see details string in Chrome logs.
467 VLOG(1) << details;
468 HelpAppLauncher::HelpTopic help_topic_id;
469 switch (login_performer_->error().state()) {
470 case GoogleServiceAuthError::CONNECTION_FAILED:
471 help_topic_id = HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE;
472 break;
473 case GoogleServiceAuthError::ACCOUNT_DISABLED:
474 help_topic_id = HelpAppLauncher::HELP_ACCOUNT_DISABLED;
475 break;
476 case GoogleServiceAuthError::HOSTED_NOT_ALLOWED:
477 help_topic_id = HelpAppLauncher::HELP_HOSTED_ACCOUNT;
478 break;
479 default:
480 help_topic_id = login_performer_->login_timed_out() ?
481 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE :
482 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT;
483 break;
484 }
485
486 login_display_->ShowError(error_id, num_login_attempts_, help_topic_id);
487 }
488
489 } // namespace chromeos
490