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 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 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 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 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 141 void ExistingUserController::CreateAccount() { 142 guest_mode_url_ = 143 google_util::AppendGoogleLocaleParam(GURL(kCreateAccountURL)); 144 LoginAsGuest(); 145 } 146 147 string16 ExistingUserController::GetConnectedNetworkName() { 148 return GetCurrentNetworkName(CrosLibrary::Get()->GetNetworkLibrary()); 149 } 150 151 void ExistingUserController::FixCaptivePortal() { 152 guest_mode_url_ = GURL(kCaptivePortalLaunchURL); 153 LoginAsGuest(); 154 } 155 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 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 217 void ExistingUserController::OnUserSelected(const std::string& username) { 218 login_performer_.reset(NULL); 219 num_login_attempts_ = 0; 220 } 221 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 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 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 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 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 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 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 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 422 void ExistingUserController::OnCaptchaEntered(const std::string& captcha) { 423 login_performer_->set_captcha(captcha); 424 } 425 426 //////////////////////////////////////////////////////////////////////////////// 427 // ExistingUserController, PasswordChangedView::Delegate implementation: 428 // 429 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 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 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 453 gfx::NativeWindow ExistingUserController::GetNativeWindow() const { 454 return host_->GetNativeWindow(); 455 } 456 457 void ExistingUserController::SetStatusAreaEnabled(bool enable) { 458 host_->SetStatusAreaEnabled(enable); 459 } 460 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