• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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