| Index: chrome/browser/net/gaia/authentication_service.cc
|
| diff --git a/chrome/browser/net/gaia/authentication_service.cc b/chrome/browser/net/gaia/authentication_service.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8828204efe675ac94c3d757c62e76d09e7e06d71
|
| --- /dev/null
|
| +++ b/chrome/browser/net/gaia/authentication_service.cc
|
| @@ -0,0 +1,261 @@
|
| +// Copyright (c) 2011 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/net/gaia/authentication_service.h"
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/logging.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
|
| +#include "chrome/common/net/gaia/gaia_constants.h"
|
| +#include "content/browser/browser_thread.h"
|
| +#include "content/common/notification_service.h"
|
| +#include "net/url_request/url_request_context_getter.h"
|
| +
|
| +// Unfortunately kNumServices must be defined in the .h.
|
| +// TODO(chron): Sync doesn't use the TalkToken anymore so we can stop
|
| +// requesting it.
|
| +const char* AuthenticationService::kServices[] = {
|
| + GaiaConstants::kGaiaService,
|
| + GaiaConstants::kSyncService,
|
| + GaiaConstants::kTalkService,
|
| + GaiaConstants::kDeviceManagementService
|
| +};
|
| +
|
| +// static
|
| +const char AuthenticationService::kClientLoginVariant[] = "ClientLogin";
|
| +const char AuthenticationService::kOAuthVariant[] = "OAuth 2.0";
|
| +
|
| +AuthenticationService::AuthenticationService()
|
| + : token_loading_query_(0) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +}
|
| +
|
| +AuthenticationService::~AuthenticationService() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + ResetCredentialsInMemory();
|
| +}
|
| +
|
| +void AuthenticationService::Initialize(const char* const source,
|
| + Profile* profile) {
|
| +
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + if (!source_.empty()) {
|
| + // Already initialized.
|
| + return;
|
| + }
|
| + getter_ = profile->GetRequestContext();
|
| + // Since the user can create a bookmark in incognito, sync may be running.
|
| + // Thus we have to go for explicit access.
|
| + web_data_service_ = profile->GetWebDataService(Profile::EXPLICIT_ACCESS);
|
| + source_ = std::string(source);
|
| +
|
| +#ifndef NDEBUG
|
| + CommandLine* cmd_line = CommandLine::ForCurrentProcess();
|
| + // Allow the token service to be cleared from the command line.
|
| + if (cmd_line->HasSwitch(switches::kClearAuthenticationService))
|
| + EraseTokensFromDB();
|
| +
|
| + // Allow a token to be injected from the command line.
|
| + if (cmd_line->HasSwitch(switches::kSetToken)) {
|
| + std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken);
|
| + int separator = value.find(':');
|
| + std::string service = value.substr(0, separator);
|
| + std::string token = value.substr(separator + 1);
|
| + token_map_[service] = token;
|
| + SaveAuthTokenToDB(service, token);
|
| + }
|
| +#endif
|
| +
|
| + registrar_.Add(this,
|
| + NotificationType::TOKEN_UPDATED,
|
| + NotificationService::AllSources());
|
| +}
|
| +
|
| +void AuthenticationService::ResetCredentialsInMemory() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + // Terminate any running fetchers. Callbacks will not return.
|
| + for (int i = 0; i < kNumServices; i++) {
|
| + fetchers_[i].reset();
|
| + }
|
| +
|
| + // Cancel pending loads. Callbacks will not return.
|
| + if (token_loading_query_) {
|
| + web_data_service_->CancelRequest(token_loading_query_);
|
| + token_loading_query_ = 0;
|
| + }
|
| +
|
| + token_map_.clear();
|
| + credentials_.reset();
|
| +}
|
| +
|
| +void AuthenticationService::UpdateCredentials(
|
| + AuthenticationConsumer::AuthenticationResult* credentials) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + credentials_.reset(credentials);
|
| +
|
| + // Cancels any currently running requests.
|
| + for (int i = 0; i < kNumServices; i++) {
|
| + // fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_));
|
| + fetchers_[i].reset(NULL);
|
| +// fetchers_[i].reset(AuthenticationFetcher::CreateAuthenticationFetcher(
|
| +// this, source_, getter_));
|
| + }
|
| +}
|
| +
|
| +void AuthenticationService::LoadTokensFromDB() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + token_loading_query_ = web_data_service_->GetAllTokens(this);
|
| +}
|
| +
|
| +void AuthenticationService::SaveAuthTokenToDB(const std::string& service,
|
| + const std::string& auth_token) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + web_data_service_->SetTokenForService(service, auth_token);
|
| +}
|
| +
|
| +void AuthenticationService::EraseTokensFromDB() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + web_data_service_->RemoveAllTokens();
|
| +}
|
| +
|
| +bool AuthenticationService::AreCredentialsValid() const {
|
| + return credentials_->IsValid();
|
| +}
|
| +
|
| +void AuthenticationService::StartFetchingTokens() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(AreCredentialsValid());
|
| + for (int i = 0; i < kNumServices; i++) {
|
| + fetchers_[i]->StartIssueAuthToken(*credentials_, kServices[i]);
|
| + }
|
| +}
|
| +
|
| +// Services dependent on a token will check if a token is available.
|
| +// If it isn't, they'll go to sleep until they get a token event.
|
| +bool AuthenticationService::HasTokenForService(
|
| + const char* const service) const {
|
| + return token_map_.count(service) > 0;
|
| +}
|
| +
|
| +const std::string& AuthenticationService::GetTokenForService(
|
| + const char* const service) const {
|
| +
|
| + if (token_map_.count(service) > 0) {
|
| + // Note map[key] is not const.
|
| + return (*token_map_.find(service)).second;
|
| + }
|
| + return EmptyString();
|
| +}
|
| +
|
| +// Note that this can fire twice or more for any given service.
|
| +// It can fire once from the DB read, and then once from the initial
|
| +// fetcher. Future fetches can cause more notification firings.
|
| +// The DB read will not however fire a notification if the fetcher
|
| +// returned first. So it's always safe to use the latest notification.
|
| +void AuthenticationService::FireTokenAvailableNotification(
|
| + const std::string& service,
|
| + const std::string& auth_token) {
|
| +
|
| + TokenAvailableDetails details(service, auth_token);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::TOKEN_AVAILABLE,
|
| + Source<AuthenticationService>(this),
|
| + Details<const TokenAvailableDetails>(&details));
|
| +}
|
| +
|
| +void AuthenticationService::FireTokenRequestFailedNotification(
|
| + const std::string& service,
|
| + const GoogleServiceAuthError& error) {
|
| +
|
| + TokenRequestFailedDetails details(service, error);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::TOKEN_REQUEST_FAILED,
|
| + Source<AuthenticationService>(this),
|
| + Details<const TokenRequestFailedDetails>(&details));
|
| +}
|
| +
|
| +void AuthenticationService::IssueAuthTokenForTest(const std::string& service,
|
| + const std::string& auth_token) {
|
| + token_map_[service] = auth_token;
|
| + FireTokenAvailableNotification(service, auth_token);
|
| +}
|
| +
|
| +void AuthenticationService::OnIssueAuthTokenSuccess(const std::string& service,
|
| + const std::string& auth_token) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + VLOG(1) << "Got an authorization token for " << service;
|
| + token_map_[service] = auth_token;
|
| + FireTokenAvailableNotification(service, auth_token);
|
| + SaveAuthTokenToDB(service, auth_token);
|
| +}
|
| +
|
| +void AuthenticationService::OnIssueAuthTokenFailure(const std::string& service,
|
| + const GoogleServiceAuthError& error) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + LOG(WARNING) << "Auth token issuing failed for service:" << service;
|
| + FireTokenRequestFailedNotification(service, error);
|
| +}
|
| +
|
| +void AuthenticationService::OnWebDataServiceRequestDone(
|
| + WebDataService::Handle h,
|
| + const WDTypedResult* result) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(token_loading_query_);
|
| + token_loading_query_ = 0;
|
| +
|
| + // If the fetch failed, there will be no result. In that case, we just don't
|
| + // load any tokens at all from the DB.
|
| + if (result) {
|
| + DCHECK(result->GetType() == TOKEN_RESULT);
|
| + const WDResult<std::map<std::string, std::string> > * token_result =
|
| + static_cast<const WDResult<std::map<std::string, std::string> > * > (
|
| + result);
|
| + LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
|
| + }
|
| +
|
| + NotificationService::current()->Notify(
|
| + NotificationType::TOKEN_LOADING_FINISHED,
|
| + Source<AuthenticationService>(this),
|
| + NotificationService::NoDetails());
|
| +}
|
| +
|
| +// Load tokens from the db_token map into the in memory token map.
|
| +void AuthenticationService::LoadTokensIntoMemory(
|
| + const std::map<std::string, std::string>& db_tokens,
|
| + std::map<std::string, std::string>* in_memory_tokens) {
|
| +
|
| + for (int i = 0; i < kNumServices; i++) {
|
| + // OnIssueAuthTokenSuccess should come from the same thread.
|
| + // If a token is already present in the map, it could only have
|
| + // come from a DB read or from IssueAuthToken. Since we should never
|
| + // fetch from the DB twice in a browser session, it must be from
|
| + // OnIssueAuthTokenSuccess, which is a live fetcher.
|
| + //
|
| + // Network fetched tokens take priority over DB tokens, so exclude tokens
|
| + // which have already been loaded by the fetcher.
|
| + if (!in_memory_tokens->count(kServices[i]) &&
|
| + db_tokens.count(kServices[i])) {
|
| + std::string db_token = db_tokens.find(kServices[i])->second;
|
| + if (!db_token.empty()) {
|
| + VLOG(1) << "Loading " << kServices[i] << "token from DB: " << db_token;
|
| + (*in_memory_tokens)[kServices[i]] = db_token;
|
| + FireTokenAvailableNotification(kServices[i], db_token);
|
| + // Failures are only for network errors.
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void AuthenticationService::Observe(NotificationType type,
|
| + const NotificationSource& source,
|
| + const NotificationDetails& details) {
|
| + DCHECK(type == NotificationType::TOKEN_UPDATED);
|
| + TokenAvailableDetails* tok_details =
|
| + Details<TokenAvailableDetails>(details).ptr();
|
| + OnIssueAuthTokenSuccess(tok_details->service(), tok_details->token());
|
| +}
|
|
|