Chromium Code Reviews| Index: chrome/browser/chromeos/login/login_performer.cc |
| diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc |
| index eb5304d2e41f9ae696f9c4729ef8cd027e95e1af..e7d54002f33fcc7790be330e31ac9277c575a8c3 100644 |
| --- a/chrome/browser/chromeos/login/login_performer.cc |
| +++ b/chrome/browser/chromeos/login/login_performer.cc |
| @@ -4,41 +4,91 @@ |
| #include "chrome/browser/chromeos/login/login_performer.h" |
| +#include <string> |
| + |
| +#include "app/l10n_util.h" |
| +#include "app/resource_bundle.h" |
| #include "base/logging.h" |
| #include "base/message_loop.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_thread.h" |
| #include "chrome/browser/chromeos/boot_times_loader.h" |
| +#include "chrome/browser/chromeos/cros/cros_library.h" |
| +#include "chrome/browser/chromeos/cros/screen_lock_library.h" |
| #include "chrome/browser/chromeos/login/login_utils.h" |
| +#include "chrome/browser/chromeos/login/screen_locker.h" |
| #include "chrome/browser/chromeos/user_cros_settings_provider.h" |
| +#include "chrome/common/notification_service.h" |
| +#include "chrome/common/notification_type.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/profile_manager.h" |
| +#include "grit/generated_resources.h" |
| namespace chromeos { |
| -namespace { |
| -} // namespace |
| +// Initialize default LoginPerformer. |
| +// static |
| +LoginPerformer* LoginPerformer::default_performer_ = NULL; |
| LoginPerformer::LoginPerformer(Delegate* delegate) |
| : last_login_failure_(LoginFailure::None()), |
| - delegate_(delegate) {} |
| + delegate_(delegate), |
| + password_changed_(false) { |
| + DCHECK(default_performer_ == NULL) |
| + << "LoginPerformer should have only one instance."; |
| + default_performer_ = this; |
| +} |
| + |
| +LoginPerformer::~LoginPerformer() { |
| + DVLOG(1) << "Deleting LoginPerformer"; |
| + DCHECK(default_performer_ != NULL) << "Default instance should exist."; |
| + default_performer_ = NULL; |
| +} |
| //////////////////////////////////////////////////////////////////////////////// |
| // LoginPerformer, LoginStatusConsumer implementation: |
| void LoginPerformer::OnLoginFailure(const LoginFailure& failure) { |
| - last_login_failure_ = failure; |
| - if (delegate_) { |
| - captcha_.clear(); |
| - captcha_token_.clear(); |
| - if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED && |
| - failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) { |
| - captcha_token_ = failure.error().captcha().token; |
| - } |
| - delegate_->OnLoginFailure(failure); |
| - } else { |
| - // TODO(nkostylev): Provide blocking UI using ScreenLocker. |
| - } |
| + DVLOG(1) << "failure.reason " << failure.reason(); |
| + DVLOG(1) << "failure.error.state " << failure.error().state(); |
| + |
| + last_login_failure_ = failure; |
| + if (delegate_) { |
| + captcha_.clear(); |
| + captcha_token_.clear(); |
| + if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED && |
| + failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) { |
| + captcha_token_ = failure.error().captcha().token; |
| + } |
| + delegate_->OnLoginFailure(failure); |
| + return; |
| + } |
| + |
| + // Consequent online login failure with blocking UI on. |
| + // No difference between cases whether screen was locked by the user or |
| + // by LoginPerformer. |
| + // Display recoverable error message using ScreenLocker, |
| + // force sign out otherwise. |
| + if (ScreenLocker::default_screen_locker()) { |
| + ResolveLockLoginFailure(); |
| + return; |
| + } |
| + |
| + // Offline auth - OK, online auth - failed. |
| + if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED) { |
| + ResolveInitialNetworkAuthFailure(); |
| + } else if (failure.reason() == LoginFailure::LOGIN_TIMED_OUT) { |
| + LOG(INFO) << "Online login timed out. " |
| + << "Granting user access based on offline auth only."; |
| + // ScreenLock is not active, it's ok to delete itself. |
| + MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| + } else { |
| + // COULD_NOT_MOUNT_CRYPTOHOME, COULD_NOT_MOUNT_TMPFS: |
| + // happens during offline auth only. |
| + // UNLOCK_FAILED is used during normal screen lock case. |
| + // TODO(nkostylev) DATA_REMOVAL_FAILED - ? |
| + NOTREACHED(); |
| + } |
| } |
| void LoginPerformer::OnLoginSuccess( |
| @@ -46,16 +96,34 @@ void LoginPerformer::OnLoginSuccess( |
| const std::string& password, |
| const GaiaAuthConsumer::ClientLoginResult& credentials, |
| bool pending_requests) { |
| + VLOG(1) << "LoginSuccess, pending_requests " << pending_requests; |
| if (delegate_) { |
| delegate_->OnLoginSuccess(username, |
| password, |
| credentials, |
| pending_requests); |
| + // After delegate_->OnLoginSuccess(...) is called, delegate_ releases |
| + // LoginPerformer ownership. LP now manages it's lifetime on its own. |
| + // 2 things could make it exist longer: |
| + // 1. ScreenLock active (pending correct new password input) |
| + // 2. Pending online auth request. |
| if (!pending_requests) |
| MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| } else { |
| - DCHECK(!pending_requests); |
| - // Online login has succeeded. Delete our instance. |
| + DCHECK(!pending_requests) |
| + << "Pending request w/o delegate_ should not happen!"; |
| + // Online login has succeeded. |
| + // TODO(nkostylev): Execute CookieFetcher->AttemptFetch() here once |
| + // async login is implemented - waiting for CL merge. |
| + if (ScreenLocker::default_screen_locker()) { |
| + DVLOG(1) << "Online login OK - unlocking screen."; |
| + RequestScreenUnlock(); |
| + // Do not delete itself just yet, wait for unlock. |
| + // See ResolveScreenUnlocked(). |
| + return; |
| + } |
| + // There's nothing else that's holding LP from deleting itself - |
| + // no ScreenLock, no pending requests. |
| MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| } |
| } |
| @@ -73,7 +141,12 @@ void LoginPerformer::OnPasswordChangeDetected( |
| if (delegate_) { |
| delegate_->OnPasswordChangeDetected(credentials); |
| } else { |
| - // TODO(nkostylev): Provide blocking UI using ScreenLocker. |
| + last_login_failure_ = |
| + LoginFailure::FromNetworkAuthFailure(GoogleServiceAuthError( |
| + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); |
| + password_changed_ = true; |
| + DVLOG(1) << "Password change detected - locking screen."; |
| + RequestScreenLock(); |
| } |
| } |
| @@ -94,13 +167,32 @@ void LoginPerformer::OnCheckWhiteListCompleted(bool success, |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| +// LoginPerformer, NotificationObserver implementation: |
| +// |
| + |
| +void LoginPerformer::Observe(NotificationType type, |
| + const NotificationSource& source, |
| + const NotificationDetails& details) { |
| + if (type != NotificationType::SCREEN_LOCK_STATE_CHANGED) |
| + return; |
| + |
| + bool is_screen_locked = *Details<bool>(details).ptr(); |
| + if (is_screen_locked) |
| + ResolveScreenLocked(); |
|
oshima
2010/11/29 20:10:10
screen can be locked even if auth succeeds if a us
Nikita (slow)
2010/12/01 12:42:57
Done.
I've added additional flag.
It might happen
|
| + else |
| + ResolveScreenUnlocked(); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| // LoginPerformer, public: |
| void LoginPerformer::Login(const std::string& username, |
| const std::string& password) { |
| username_ = username; |
| password_ = password; |
| - if (UserCrosSettingsProvider::cached_allow_new_user()) { |
| + // Whitelist check is performed during offline login only. |
| + if (ScreenLocker::default_screen_locker() || |
| + UserCrosSettingsProvider::cached_allow_new_user()) { |
| // Starts authentication if guest login is allowed. |
| StartAuthentication(); |
| } else { |
| @@ -140,19 +232,180 @@ void LoginPerformer::ResyncEncryptedData() { |
| //////////////////////////////////////////////////////////////////////////////// |
| // LoginPerformer, private: |
| +void LoginPerformer::RequestScreenLock() { |
| + DVLOG(1) << "Screen lock requested"; |
| + if (ScreenLocker::default_screen_locker()) { |
| + DVLOG(1) << "Screen already locked"; |
| + ResolveScreenLocked(); |
| + } else { |
| + registrar_.Add( |
| + this, |
| + NotificationType::SCREEN_LOCK_STATE_CHANGED, |
| + NotificationService::AllSources()); |
| + chromeos::CrosLibrary::Get()->GetScreenLockLibrary()-> |
| + NotifyScreenLockRequested(); |
| + } |
| +} |
| + |
| +void LoginPerformer::RequestScreenUnlock() { |
| + DVLOG(1) << "Screen unlock requested"; |
| + if (ScreenLocker::default_screen_locker()) { |
| + chromeos::CrosLibrary::Get()->GetScreenLockLibrary()-> |
| + NotifyScreenUnlockRequested(); |
| + // Will unsubscribe from notifications once unlock is successful. |
| + } else { |
| + LOG(ERROR) << "Screen is not locked"; |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +void LoginPerformer::ResolveInitialNetworkAuthFailure() { |
| + DVLOG(1) << "auth_error: " << last_login_failure_.error().state(); |
| + |
| + switch (last_login_failure_.error().state()) { |
| + case GoogleServiceAuthError::CONNECTION_FAILED: |
| + case GoogleServiceAuthError::SERVICE_UNAVAILABLE: |
| + case GoogleServiceAuthError::TWO_FACTOR: |
| + case GoogleServiceAuthError::REQUEST_CANCELED: |
| + // Offline auth already done. Online auth will be done next time |
| + // or once user accesses web property. |
| + LOG(INFO) << "Granting user access based on offline auth only. " |
| + << "Online login failed with " |
| + << last_login_failure_.error().state(); |
| + // Resolving initial online auth failure, no ScreenLock is active. |
| + MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| + return; |
| + case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: |
| + // Offline auth OK, so it might be the case of changed password. |
| + password_changed_ = true; |
| + case GoogleServiceAuthError::USER_NOT_SIGNED_UP: |
| + case GoogleServiceAuthError::ACCOUNT_DELETED: |
| + case GoogleServiceAuthError::ACCOUNT_DISABLED: |
| + // Access not granted. User has to sign out. |
| + // Request screen lock & show error message there. |
| + case GoogleServiceAuthError::CAPTCHA_REQUIRED: |
| + // User is requested to enter CAPTCHA challenge. |
| + RequestScreenLock(); |
| + return; |
| + default: |
| + // Unless there's new GoogleServiceAuthErrors state has been added. |
| + NOTREACHED(); |
| + return; |
| + } |
| +} |
| + |
| +void LoginPerformer::ResolveLockLoginFailure() { |
| + if (last_login_failure_.reason() == LoginFailure::LOGIN_TIMED_OUT) { |
| + LOG(INFO) << "Online login timed out - unlocking screen. " |
| + << "Granting user access based on offline auth only."; |
| + RequestScreenUnlock(); |
| + return; |
| + } else if (last_login_failure_.reason() == |
| + LoginFailure::NETWORK_AUTH_FAILED) { |
| + ResolveLockNetworkAuthFailure(); |
| + return; |
| + } else if (last_login_failure_.reason() == |
| + LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME || |
| + last_login_failure_.reason() == |
| + LoginFailure::DATA_REMOVAL_FAILED) { |
| + LOG(ERROR) << "Cryptohome error, forcing sign out."; |
| + } else { |
| + // COULD_NOT_MOUNT_TMPFS, UNLOCK_FAILED should not happen here. |
| + NOTREACHED(); |
| + } |
| + ScreenLocker::default_screen_locker()->Signout(); |
| +} |
| + |
| +void LoginPerformer::ResolveLockNetworkAuthFailure() { |
| + DCHECK(ScreenLocker::default_screen_locker()) |
| + << "ScreenLocker instance doesn't exist."; |
| + DCHECK(last_login_failure_.reason() == LoginFailure::NETWORK_AUTH_FAILED); |
| + |
| + std::wstring msg; |
| + bool sign_out_only = false; |
| + |
| + DVLOG(1) << "auth_error: " << last_login_failure_.error().state(); |
| + |
| + switch (last_login_failure_.error().state()) { |
| + case GoogleServiceAuthError::CONNECTION_FAILED: |
| + case GoogleServiceAuthError::SERVICE_UNAVAILABLE: |
| + case GoogleServiceAuthError::TWO_FACTOR: |
| + case GoogleServiceAuthError::REQUEST_CANCELED: |
| + // Offline auth already done. Online auth will be done next time |
| + // or once user accesses web property. |
| + LOG(INFO) << "Granting user access based on offline auth only. " |
| + << "Online login failed with " |
| + << last_login_failure_.error().state(); |
| + RequestScreenUnlock(); |
| + return; |
| + case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: |
| + // Password change detected. |
| + msg = l10n_util::GetString(IDS_LOGIN_ERROR_PASSWORD_CHANGED); |
| + break; |
| + case GoogleServiceAuthError::USER_NOT_SIGNED_UP: |
| + case GoogleServiceAuthError::ACCOUNT_DELETED: |
| + case GoogleServiceAuthError::ACCOUNT_DISABLED: |
| + // Access not granted. User has to sign out. |
| + // Show error message using existing screen lock. |
| + msg = l10n_util::GetString(IDS_LOGIN_ERROR_RESTRICTED); |
| + sign_out_only = true; |
| + break; |
| + case GoogleServiceAuthError::CAPTCHA_REQUIRED: |
| + // User is requested to enter CAPTCHA challenge. |
| + msg = l10n_util::GetString(IDS_LOGIN_ERROR_AUTHENTICATING); |
| + // TODO(nkostylev): Instruct ScreenLocker to show CAPTCHA input. |
| + break; |
| + default: |
| + // Unless there's new GoogleServiceAuthError state has been added. |
| + NOTREACHED(); |
| + break; |
| + } |
| + |
| + ScreenLocker::default_screen_locker()->ShowErrorMessage(msg, sign_out_only); |
| +} |
| + |
| +void LoginPerformer::ResolveScreenLocked() { |
| + DVLOG(1) << "Screen locked"; |
| + ResolveLockNetworkAuthFailure(); |
| +} |
| + |
| +void LoginPerformer::ResolveScreenUnlocked() { |
| + DVLOG(1) << "Screen unlocked"; |
| + registrar_.RemoveAll(); |
| + // If screen was unlocked that was for a reason, should delete itself now. |
| + MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| +} |
| + |
| void LoginPerformer::StartAuthentication() { |
| + DVLOG(1) << "Auth started"; |
| BootTimesLoader::Get()->AddLoginTimeMarker("AuthStarted", false); |
| - authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); |
| Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile(); |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, FROM_HERE, |
| - NewRunnableMethod(authenticator_.get(), |
| - &Authenticator::AuthenticateToLogin, |
| - profile, |
| - username_, |
| - password_, |
| - captcha_token_, |
| - captcha_)); |
| + if (delegate_) { |
| + authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + NewRunnableMethod(authenticator_.get(), |
| + &Authenticator::AuthenticateToLogin, |
| + profile, |
| + username_, |
| + password_, |
| + captcha_token_, |
| + captcha_)); |
| + } else { |
| + DCHECK(authenticator_.get()) |
| + << "Authenticator instance doesn't exist for login attempt retry."; |
| + // At this point offline auth has been successful, |
| + // retry online auth, using existing Authenticator instance. |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + NewRunnableMethod(authenticator_.get(), |
| + &Authenticator::RetryAuth, |
| + profile, |
| + username_, |
| + password_, |
| + captcha_token_, |
| + captcha_)); |
| + } |
| password_.clear(); |
| } |