// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/login/online_attempt.h" #include <string> #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/login/auth_attempt_state.h" #include "chrome/browser/chromeos/login/auth_attempt_state_resolver.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "content/public/browser/browser_thread.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_constants.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" #include "third_party/libjingle/source/talk/base/urlencode.h" using content::BrowserThread; namespace chromeos { // static const int OnlineAttempt::kClientLoginTimeoutMs = 10000; OnlineAttempt::OnlineAttempt(AuthAttemptState* current_attempt, AuthAttemptStateResolver* callback) : attempt_(current_attempt), resolver_(callback), weak_factory_(this), try_again_(true) { DCHECK(attempt_->user_type == User::USER_TYPE_REGULAR); } OnlineAttempt::~OnlineAttempt() { // Just to be sure. if (client_fetcher_.get()) client_fetcher_->CancelRequest(); } void OnlineAttempt::Initiate(Profile* auth_profile) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); client_fetcher_.reset( new GaiaAuthFetcher(this, GaiaConstants::kChromeOSSource, auth_profile->GetRequestContext())); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&OnlineAttempt::TryClientLogin, weak_factory_.GetWeakPtr())); } void OnlineAttempt::OnClientLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& unused) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << "Online login successful!"; weak_factory_.InvalidateWeakPtrs(); if (attempt_->hosted_policy() == GaiaAuthFetcher::HostedAccountsAllowed && attempt_->is_first_time_user()) { // First time user, and we don't know if the account is HOSTED or not. // Since we don't allow HOSTED accounts to log in, we need to try // again, without allowing HOSTED accounts. // // NOTE: we used to do this in the opposite order, so that we'd only // try the HOSTED pathway if GOOGLE-only failed. This breaks CAPTCHA // handling, though. attempt_->DisableHosted(); TryClientLogin(); return; } TriggerResolve(LoginFailure::LoginFailureNone()); } void OnlineAttempt::OnClientLoginFailure( const GoogleServiceAuthError& error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); weak_factory_.InvalidateWeakPtrs(); if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) { if (try_again_) { try_again_ = false; // TODO(cmasone): add UMA tracking for this to see if we can remove it. LOG(ERROR) << "Login attempt canceled!?!? Trying again."; TryClientLogin(); return; } LOG(ERROR) << "Login attempt canceled again? Already retried..."; } if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS && attempt_->is_first_time_user() && attempt_->hosted_policy() != GaiaAuthFetcher::HostedAccountsAllowed) { // This was a first-time login, we already tried allowing HOSTED accounts // and succeeded. That we've failed with INVALID_GAIA_CREDENTIALS now // indicates that the account is HOSTED. LOG(WARNING) << "Rejecting valid HOSTED account."; TriggerResolve(LoginFailure::FromNetworkAuthFailure( GoogleServiceAuthError( GoogleServiceAuthError::HOSTED_NOT_ALLOWED))); return; } if (error.state() == GoogleServiceAuthError::TWO_FACTOR) { LOG(WARNING) << "Two factor authenticated. Sync will not work."; TriggerResolve(LoginFailure::LoginFailureNone()); return; } VLOG(2) << "ClientLogin attempt failed with " << error.state(); TriggerResolve(LoginFailure::FromNetworkAuthFailure(error)); } void OnlineAttempt::TryClientLogin() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&OnlineAttempt::CancelClientLogin, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kClientLoginTimeoutMs)); client_fetcher_->StartClientLogin( attempt_->user_context.username, attempt_->user_context.password, GaiaConstants::kSyncService, attempt_->login_token, attempt_->login_captcha, attempt_->hosted_policy()); } bool OnlineAttempt::HasPendingFetch() { return client_fetcher_->HasPendingFetch(); } void OnlineAttempt::CancelRequest() { weak_factory_.InvalidateWeakPtrs(); } void OnlineAttempt::CancelClientLogin() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (HasPendingFetch()) { LOG(WARNING) << "Canceling ClientLogin attempt."; CancelRequest(); TriggerResolve(LoginFailure(LoginFailure::LOGIN_TIMED_OUT)); } } void OnlineAttempt::TriggerResolve( const LoginFailure& outcome) { attempt_->RecordOnlineLoginStatus(outcome); client_fetcher_.reset(NULL); resolver_->Resolve(); } } // namespace chromeos