| 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 1f10bfb7a02b7823ce2adcf9d9fcffa9402e38b8..4c5749bc7cf98675c828674b09ae60f7aff15659 100644
|
| --- a/chrome/browser/chromeos/login/login_performer.cc
|
| +++ b/chrome/browser/chromeos/login/login_performer.cc
|
| @@ -4,42 +4,93 @@
|
|
|
| #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),
|
| - method_factory_(this) {}
|
| + password_changed_(false),
|
| + screen_lock_requested_(false),
|
| + method_factory_(this) {
|
| + 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) {
|
| + VLOG(1) << "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(
|
| @@ -47,16 +98,35 @@ 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.
|
| + // http://crosbug.com/9814
|
| + 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);
|
| }
|
| }
|
| @@ -74,7 +144,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();
|
| }
|
| }
|
|
|
| @@ -95,26 +170,53 @@ 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) {
|
| + if (screen_lock_requested_) {
|
| + screen_lock_requested_ = false;
|
| + ResolveScreenLocked();
|
| + }
|
| + } else {
|
| + ResolveScreenUnlocked();
|
| + }
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| // LoginPerformer, public:
|
|
|
| void LoginPerformer::Login(const std::string& username,
|
| const std::string& password) {
|
| username_ = username;
|
| password_ = password;
|
| - // Must not proceed without signature verification.
|
| - UserCrosSettingsProvider user_settings;
|
| - bool trusted_setting_available = user_settings.RequestTrustedAllowNewUser(
|
| - method_factory_.NewRunnableMethod(
|
| - &LoginPerformer::Login,
|
| - username,
|
| - password));
|
| - if (!trusted_setting_available) {
|
| - // Value of AllowNewUser setting is still not verified.
|
| - // Another attempt will be invoked after verification completion.
|
| - return;
|
| +
|
| + // Whitelist check is always performed during initial login and
|
| + // should not be performed when ScreenLock is active (pending online auth).
|
| + if (!ScreenLocker::default_screen_locker()) {
|
| + // Must not proceed without signature verification.
|
| + UserCrosSettingsProvider user_settings;
|
| + bool trusted_setting_available = user_settings.RequestTrustedAllowNewUser(
|
| + method_factory_.NewRunnableMethod(&LoginPerformer::Login,
|
| + username,
|
| + password));
|
| + if (!trusted_setting_available) {
|
| + // Value of AllowNewUser setting is still not verified.
|
| + // Another attempt will be invoked after verification completion.
|
| + return;
|
| + }
|
| }
|
| - if (UserCrosSettingsProvider::cached_allow_new_user()) {
|
| - // Starts authentication if guest login is allowed.
|
| +
|
| + if (ScreenLocker::default_screen_locker() ||
|
| + UserCrosSettingsProvider::cached_allow_new_user()) {
|
| + // Starts authentication if guest login is allowed or online auth pending.
|
| StartAuthentication();
|
| } else {
|
| // Otherwise, do whitelist check first.
|
| @@ -153,19 +255,182 @@ 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 {
|
| + screen_lock_requested_ = true;
|
| + 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.
|
| + VLOG(1) << "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(WARNING) << "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(WARNING) << "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.
|
| + // http://crosbug.com/9812
|
| + 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();
|
| }
|
|
|
|
|