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()); |
+} |