login_performer.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/login_performer.h" 6 7#include <string> 8 9#include "app/l10n_util.h" 10#include "app/resource_bundle.h" 11#include "base/logging.h" 12#include "base/message_loop.h" 13#include "base/metrics/histogram.h" 14#include "chrome/browser/browser_process.h" 15#include "chrome/browser/browser_thread.h" 16#include "chrome/browser/chromeos/boot_times_loader.h" 17#include "chrome/browser/chromeos/cros/cros_library.h" 18#include "chrome/browser/chromeos/cros/screen_lock_library.h" 19#include "chrome/browser/chromeos/login/login_utils.h" 20#include "chrome/browser/chromeos/login/screen_locker.h" 21#include "chrome/browser/chromeos/user_cros_settings_provider.h" 22#include "chrome/browser/metrics/user_metrics.h" 23#include "chrome/browser/profiles/profile.h" 24#include "chrome/browser/profiles/profile_manager.h" 25#include "chrome/common/notification_service.h" 26#include "chrome/common/notification_type.h" 27#include "grit/generated_resources.h" 28 29namespace chromeos { 30 31// Initialize default LoginPerformer. 32// static 33LoginPerformer* LoginPerformer::default_performer_ = NULL; 34 35LoginPerformer::LoginPerformer(Delegate* delegate) 36 : last_login_failure_(LoginFailure::None()), 37 delegate_(delegate), 38 password_changed_(false), 39 screen_lock_requested_(false), 40 initial_online_auth_pending_(false), 41 method_factory_(this) { 42 DCHECK(default_performer_ == NULL) 43 << "LoginPerformer should have only one instance."; 44 default_performer_ = this; 45} 46 47LoginPerformer::~LoginPerformer() { 48 DVLOG(1) << "Deleting LoginPerformer"; 49 DCHECK(default_performer_ != NULL) << "Default instance should exist."; 50 default_performer_ = NULL; 51} 52 53//////////////////////////////////////////////////////////////////////////////// 54// LoginPerformer, LoginStatusConsumer implementation: 55 56void LoginPerformer::OnLoginFailure(const LoginFailure& failure) { 57 UserMetrics::RecordAction(UserMetricsAction("Login_Failure")); 58 UMA_HISTOGRAM_ENUMERATION("Login.FailureReason", failure.reason(), 59 LoginFailure::NUM_FAILURE_REASONS); 60 61 DVLOG(1) << "failure.reason " << failure.reason(); 62 DVLOG(1) << "failure.error.state " << failure.error().state(); 63 64 last_login_failure_ = failure; 65 if (delegate_) { 66 captcha_.clear(); 67 captcha_token_.clear(); 68 if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED && 69 failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) { 70 captcha_token_ = failure.error().captcha().token; 71 } 72 delegate_->OnLoginFailure(failure); 73 return; 74 } 75 76 // Consequent online login failure with blocking UI on. 77 // No difference between cases whether screen was locked by the user or 78 // by LoginPerformer except for the very first screen lock while waiting 79 // for online auth. Otherwise it will be SL active > timeout > screen unlock. 80 // Display recoverable error message using ScreenLocker, 81 // force sign out otherwise. 82 if (ScreenLocker::default_screen_locker() && !initial_online_auth_pending_) { 83 ResolveLockLoginFailure(); 84 return; 85 } 86 initial_online_auth_pending_ = false; 87 88 // Offline auth - OK, online auth - failed. 89 if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED) { 90 ResolveInitialNetworkAuthFailure(); 91 } else if (failure.reason() == LoginFailure::LOGIN_TIMED_OUT) { 92 VLOG(1) << "Online login timed out. " 93 << "Granting user access based on offline auth only."; 94 // ScreenLock is not active, it's ok to delete itself. 95 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 96 } else { 97 // COULD_NOT_MOUNT_CRYPTOHOME, COULD_NOT_MOUNT_TMPFS: 98 // happens during offline auth only. 99 // UNLOCK_FAILED is used during normal screen lock case. 100 // TODO(nkostylev) DATA_REMOVAL_FAILED - ? 101 NOTREACHED(); 102 } 103} 104 105void LoginPerformer::OnLoginSuccess( 106 const std::string& username, 107 const std::string& password, 108 const GaiaAuthConsumer::ClientLoginResult& credentials, 109 bool pending_requests) { 110 UserMetrics::RecordAction(UserMetricsAction("Login_Success")); 111 // 0 - Login success offline and online. It's a new user. or it's an 112 // existing user and offline auth took longer than online auth. 113 // 1 - Login success offline only. It's an existing user login. 114 UMA_HISTOGRAM_ENUMERATION("Login.SuccessReason", pending_requests, 2); 115 116 VLOG(1) << "LoginSuccess, pending_requests " << pending_requests; 117 if (delegate_) { 118 // After delegate_->OnLoginSuccess(...) is called, delegate_ releases 119 // LoginPerformer ownership. LP now manages it's lifetime on its own. 120 // 2 things could make it exist longer: 121 // 1. ScreenLock active (pending correct new password input) 122 // 2. Pending online auth request. 123 if (!pending_requests) 124 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 125 else 126 initial_online_auth_pending_ = true; 127 128 delegate_->OnLoginSuccess(username, 129 password, 130 credentials, 131 pending_requests); 132 return; 133 } else { 134 DCHECK(!pending_requests) 135 << "Pending request w/o delegate_ should not happen!"; 136 // Online login has succeeded. 137 Profile* profile = 138 g_browser_process->profile_manager()->GetDefaultProfile(); 139 LoginUtils::Get()->FetchCookies(profile, credentials); 140 LoginUtils::Get()->FetchTokens(profile, credentials); 141 142 // Don't unlock screen if it was locked while we're waiting 143 // for initial online auth. 144 if (ScreenLocker::default_screen_locker() && 145 !initial_online_auth_pending_) { 146 DVLOG(1) << "Online login OK - unlocking screen."; 147 RequestScreenUnlock(); 148 // Do not delete itself just yet, wait for unlock. 149 // See ResolveScreenUnlocked(). 150 return; 151 } 152 initial_online_auth_pending_ = false; 153 // There's nothing else that's holding LP from deleting itself - 154 // no ScreenLock, no pending requests. 155 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 156 } 157} 158 159void LoginPerformer::OnOffTheRecordLoginSuccess() { 160 UserMetrics::RecordAction( 161 UserMetricsAction("Login_GuestLoginSuccess")); 162 163 if (delegate_) 164 delegate_->OnOffTheRecordLoginSuccess(); 165 else 166 NOTREACHED(); 167} 168 169void LoginPerformer::OnPasswordChangeDetected( 170 const GaiaAuthConsumer::ClientLoginResult& credentials) { 171 cached_credentials_ = credentials; 172 if (delegate_) { 173 delegate_->OnPasswordChangeDetected(credentials); 174 } else { 175 last_login_failure_ = 176 LoginFailure::FromNetworkAuthFailure(GoogleServiceAuthError( 177 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 178 password_changed_ = true; 179 DVLOG(1) << "Password change detected - locking screen."; 180 RequestScreenLock(); 181 } 182} 183 184//////////////////////////////////////////////////////////////////////////////// 185// LoginPerformer, SignedSettingsHelper::Callback implementation: 186 187void LoginPerformer::OnCheckWhitelistCompleted(SignedSettings::ReturnCode code, 188 const std::string& email) { 189 if (code == SignedSettings::SUCCESS) { 190 // Whitelist check passed, continue with authentication. 191 StartAuthentication(); 192 } else { 193 if (delegate_) 194 delegate_->WhiteListCheckFailed(email); 195 else 196 NOTREACHED(); 197 } 198} 199 200//////////////////////////////////////////////////////////////////////////////// 201// LoginPerformer, NotificationObserver implementation: 202// 203 204void LoginPerformer::Observe(NotificationType type, 205 const NotificationSource& source, 206 const NotificationDetails& details) { 207 if (type != NotificationType::SCREEN_LOCK_STATE_CHANGED) 208 return; 209 210 bool is_screen_locked = *Details<bool>(details).ptr(); 211 if (is_screen_locked) { 212 if (screen_lock_requested_) { 213 screen_lock_requested_ = false; 214 ResolveScreenLocked(); 215 } 216 } else { 217 ResolveScreenUnlocked(); 218 } 219} 220 221//////////////////////////////////////////////////////////////////////////////// 222// LoginPerformer, public: 223 224void LoginPerformer::Login(const std::string& username, 225 const std::string& password) { 226 username_ = username; 227 password_ = password; 228 229 // Whitelist check is always performed during initial login and 230 // should not be performed when ScreenLock is active (pending online auth). 231 if (!ScreenLocker::default_screen_locker()) { 232 // Must not proceed without signature verification. 233 UserCrosSettingsProvider user_settings; 234 bool trusted_setting_available = user_settings.RequestTrustedAllowNewUser( 235 method_factory_.NewRunnableMethod(&LoginPerformer::Login, 236 username, 237 password)); 238 if (!trusted_setting_available) { 239 // Value of AllowNewUser setting is still not verified. 240 // Another attempt will be invoked after verification completion. 241 return; 242 } 243 } 244 245 if (ScreenLocker::default_screen_locker() || 246 UserCrosSettingsProvider::cached_allow_new_user()) { 247 // Starts authentication if guest login is allowed or online auth pending. 248 StartAuthentication(); 249 } else { 250 // Otherwise, do whitelist check first. 251 SignedSettingsHelper::Get()->StartCheckWhitelistOp( 252 username, this); 253 } 254} 255 256void LoginPerformer::LoginOffTheRecord() { 257 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 258 BrowserThread::PostTask( 259 BrowserThread::UI, FROM_HERE, 260 NewRunnableMethod(authenticator_.get(), 261 &Authenticator::LoginOffTheRecord)); 262} 263 264void LoginPerformer::RecoverEncryptedData(const std::string& old_password) { 265 BrowserThread::PostTask( 266 BrowserThread::UI, FROM_HERE, 267 NewRunnableMethod(authenticator_.get(), 268 &Authenticator::RecoverEncryptedData, 269 old_password, 270 cached_credentials_)); 271 cached_credentials_ = GaiaAuthConsumer::ClientLoginResult(); 272} 273 274void LoginPerformer::ResyncEncryptedData() { 275 BrowserThread::PostTask( 276 BrowserThread::UI, FROM_HERE, 277 NewRunnableMethod(authenticator_.get(), 278 &Authenticator::ResyncEncryptedData, 279 cached_credentials_)); 280 cached_credentials_ = GaiaAuthConsumer::ClientLoginResult(); 281} 282 283//////////////////////////////////////////////////////////////////////////////// 284// LoginPerformer, private: 285 286void LoginPerformer::RequestScreenLock() { 287 DVLOG(1) << "Screen lock requested"; 288 // Will receive notifications on screen unlock and delete itself. 289 registrar_.Add(this, 290 NotificationType::SCREEN_LOCK_STATE_CHANGED, 291 NotificationService::AllSources()); 292 if (ScreenLocker::default_screen_locker()) { 293 DVLOG(1) << "Screen already locked"; 294 ResolveScreenLocked(); 295 } else { 296 screen_lock_requested_ = true; 297 chromeos::CrosLibrary::Get()->GetScreenLockLibrary()-> 298 NotifyScreenLockRequested(); 299 } 300} 301 302void LoginPerformer::RequestScreenUnlock() { 303 DVLOG(1) << "Screen unlock requested"; 304 if (ScreenLocker::default_screen_locker()) { 305 chromeos::CrosLibrary::Get()->GetScreenLockLibrary()-> 306 NotifyScreenUnlockRequested(); 307 // Will unsubscribe from notifications once unlock is successful. 308 } else { 309 LOG(ERROR) << "Screen is not locked"; 310 NOTREACHED(); 311 } 312} 313 314void LoginPerformer::ResolveInitialNetworkAuthFailure() { 315 DVLOG(1) << "auth_error: " << last_login_failure_.error().state(); 316 317 switch (last_login_failure_.error().state()) { 318 case GoogleServiceAuthError::CONNECTION_FAILED: 319 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: 320 case GoogleServiceAuthError::TWO_FACTOR: 321 case GoogleServiceAuthError::REQUEST_CANCELED: 322 // Offline auth already done. Online auth will be done next time 323 // or once user accesses web property. 324 VLOG(1) << "Granting user access based on offline auth only. " 325 << "Online login failed with " 326 << last_login_failure_.error().state(); 327 // Resolving initial online auth failure, no ScreenLock is active. 328 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 329 return; 330 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 331 // Offline auth OK, so it might be the case of changed password. 332 password_changed_ = true; 333 case GoogleServiceAuthError::USER_NOT_SIGNED_UP: 334 case GoogleServiceAuthError::ACCOUNT_DELETED: 335 case GoogleServiceAuthError::ACCOUNT_DISABLED: 336 // Access not granted. User has to sign out. 337 // Request screen lock & show error message there. 338 case GoogleServiceAuthError::CAPTCHA_REQUIRED: 339 // User is requested to enter CAPTCHA challenge. 340 RequestScreenLock(); 341 return; 342 default: 343 // Unless there's new GoogleServiceAuthErrors state has been added. 344 NOTREACHED(); 345 return; 346 } 347} 348 349void LoginPerformer::ResolveLockLoginFailure() { 350 if (last_login_failure_.reason() == LoginFailure::LOGIN_TIMED_OUT) { 351 LOG(WARNING) << "Online login timed out - unlocking screen. " 352 << "Granting user access based on offline auth only."; 353 RequestScreenUnlock(); 354 return; 355 } else if (last_login_failure_.reason() == 356 LoginFailure::NETWORK_AUTH_FAILED) { 357 ResolveLockNetworkAuthFailure(); 358 return; 359 } else if (last_login_failure_.reason() == 360 LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME || 361 last_login_failure_.reason() == 362 LoginFailure::DATA_REMOVAL_FAILED) { 363 LOG(ERROR) << "Cryptohome error, forcing sign out."; 364 } else { 365 // COULD_NOT_MOUNT_TMPFS, UNLOCK_FAILED should not happen here. 366 NOTREACHED(); 367 } 368 ScreenLocker::default_screen_locker()->Signout(); 369} 370 371void LoginPerformer::ResolveLockNetworkAuthFailure() { 372 DCHECK(ScreenLocker::default_screen_locker()) 373 << "ScreenLocker instance doesn't exist."; 374 DCHECK(last_login_failure_.reason() == LoginFailure::NETWORK_AUTH_FAILED); 375 376 std::wstring msg; 377 bool sign_out_only = false; 378 379 DVLOG(1) << "auth_error: " << last_login_failure_.error().state(); 380 381 switch (last_login_failure_.error().state()) { 382 case GoogleServiceAuthError::CONNECTION_FAILED: 383 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: 384 case GoogleServiceAuthError::TWO_FACTOR: 385 case GoogleServiceAuthError::REQUEST_CANCELED: 386 // Offline auth already done. Online auth will be done next time 387 // or once user accesses web property. 388 LOG(WARNING) << "Granting user access based on offline auth only. " 389 << "Online login failed with " 390 << last_login_failure_.error().state(); 391 RequestScreenUnlock(); 392 return; 393 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 394 // Password change detected. 395 msg = l10n_util::GetString(IDS_LOGIN_ERROR_PASSWORD_CHANGED); 396 break; 397 case GoogleServiceAuthError::USER_NOT_SIGNED_UP: 398 case GoogleServiceAuthError::ACCOUNT_DELETED: 399 case GoogleServiceAuthError::ACCOUNT_DISABLED: 400 // Access not granted. User has to sign out. 401 // Show error message using existing screen lock. 402 msg = l10n_util::GetString(IDS_LOGIN_ERROR_RESTRICTED); 403 sign_out_only = true; 404 break; 405 case GoogleServiceAuthError::CAPTCHA_REQUIRED: 406 // User is requested to enter CAPTCHA challenge. 407 msg = l10n_util::GetString(IDS_LOGIN_ERROR_PASSWORD_CHANGED); 408 ScreenLocker::default_screen_locker()->ShowCaptchaAndErrorMessage( 409 last_login_failure_.error().captcha().image_url, 410 msg); 411 return; 412 default: 413 // Unless there's new GoogleServiceAuthError state has been added. 414 NOTREACHED(); 415 break; 416 } 417 418 ScreenLocker::default_screen_locker()->ShowErrorMessage(msg, sign_out_only); 419} 420 421void LoginPerformer::ResolveScreenLocked() { 422 DVLOG(1) << "Screen locked"; 423 ResolveLockNetworkAuthFailure(); 424} 425 426void LoginPerformer::ResolveScreenUnlocked() { 427 DVLOG(1) << "Screen unlocked"; 428 registrar_.RemoveAll(); 429 // If screen was unlocked that was for a reason, should delete itself now. 430 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 431} 432 433void LoginPerformer::StartAuthentication() { 434 DVLOG(1) << "Auth started"; 435 BootTimesLoader::Get()->AddLoginTimeMarker("AuthStarted", false); 436 Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile(); 437 if (delegate_) { 438 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 439 BrowserThread::PostTask( 440 BrowserThread::UI, FROM_HERE, 441 NewRunnableMethod(authenticator_.get(), 442 &Authenticator::AuthenticateToLogin, 443 profile, 444 username_, 445 password_, 446 captcha_token_, 447 captcha_)); 448 } else { 449 DCHECK(authenticator_.get()) 450 << "Authenticator instance doesn't exist for login attempt retry."; 451 // At this point offline auth has been successful, 452 // retry online auth, using existing Authenticator instance. 453 BrowserThread::PostTask( 454 BrowserThread::UI, FROM_HERE, 455 NewRunnableMethod(authenticator_.get(), 456 &Authenticator::RetryAuth, 457 profile, 458 username_, 459 password_, 460 captcha_token_, 461 captcha_)); 462 } 463 password_.clear(); 464} 465 466} // namespace chromeos 467