• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/net/gaia/token_service.h"
6 
7 #include "base/command_line.h"
8 #include "base/string_util.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "chrome/common/net/gaia/gaia_auth_fetcher.h"
12 #include "chrome/common/net/gaia/gaia_constants.h"
13 #include "content/browser/browser_thread.h"
14 #include "content/common/notification_service.h"
15 #include "net/url_request/url_request_context_getter.h"
16 
17 // Unfortunately kNumServices must be defined in the .h.
18 // TODO(chron): Sync doesn't use the TalkToken anymore so we can stop
19 //              requesting it.
20 const char* TokenService::kServices[] = {
21   GaiaConstants::kGaiaService,
22   GaiaConstants::kSyncService,
23   GaiaConstants::kTalkService,
24   GaiaConstants::kDeviceManagementService
25 };
26 
TokenService()27 TokenService::TokenService()
28     : token_loading_query_(0) {
29   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
30 }
31 
~TokenService()32 TokenService::~TokenService() {
33   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
34   ResetCredentialsInMemory();
35 }
36 
Initialize(const char * const source,Profile * profile)37 void TokenService::Initialize(const char* const source,
38                               Profile* profile) {
39 
40   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41   if (!source_.empty()) {
42     // Already initialized.
43     return;
44   }
45   getter_ = profile->GetRequestContext();
46   // Since the user can create a bookmark in incognito, sync may be running.
47   // Thus we have to go for explicit access.
48   web_data_service_ = profile->GetWebDataService(Profile::EXPLICIT_ACCESS);
49   source_ = std::string(source);
50 
51 #ifndef NDEBUG
52   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
53   // Allow the token service to be cleared from the command line.
54   if (cmd_line->HasSwitch(switches::kClearTokenService))
55     EraseTokensFromDB();
56 
57   // Allow a token to be injected from the command line.
58   if (cmd_line->HasSwitch(switches::kSetToken)) {
59     std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken);
60     int separator = value.find(':');
61     std::string service = value.substr(0, separator);
62     std::string token = value.substr(separator + 1);
63     token_map_[service] = token;
64     SaveAuthTokenToDB(service, token);
65   }
66 #endif
67 
68   registrar_.Add(this,
69                  NotificationType::TOKEN_UPDATED,
70                  NotificationService::AllSources());
71 }
72 
ResetCredentialsInMemory()73 void TokenService::ResetCredentialsInMemory() {
74   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75 
76   // Terminate any running fetchers. Callbacks will not return.
77   for (int i = 0; i < kNumServices; i++) {
78     fetchers_[i].reset();
79   }
80 
81   // Cancel pending loads. Callbacks will not return.
82   if (token_loading_query_) {
83     web_data_service_->CancelRequest(token_loading_query_);
84     token_loading_query_ = 0;
85   }
86 
87   token_map_.clear();
88   credentials_ = GaiaAuthConsumer::ClientLoginResult();
89 }
90 
UpdateCredentials(const GaiaAuthConsumer::ClientLoginResult & credentials)91 void TokenService::UpdateCredentials(
92     const GaiaAuthConsumer::ClientLoginResult& credentials) {
93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94   credentials_ = credentials;
95 
96   // Cancels any currently running requests.
97   for (int i = 0; i < kNumServices; i++) {
98     fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_));
99   }
100 }
101 
LoadTokensFromDB()102 void TokenService::LoadTokensFromDB() {
103   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104   token_loading_query_ = web_data_service_->GetAllTokens(this);
105 }
106 
SaveAuthTokenToDB(const std::string & service,const std::string & auth_token)107 void TokenService::SaveAuthTokenToDB(const std::string& service,
108                                      const std::string& auth_token) {
109   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110   web_data_service_->SetTokenForService(service, auth_token);
111 }
112 
EraseTokensFromDB()113 void TokenService::EraseTokensFromDB() {
114   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115   web_data_service_->RemoveAllTokens();
116 }
117 
AreCredentialsValid() const118 bool TokenService::AreCredentialsValid() const {
119   return !credentials_.lsid.empty() && !credentials_.sid.empty();
120 }
121 
HasLsid() const122 bool TokenService::HasLsid() const {
123   return !credentials_.lsid.empty();
124 }
125 
GetLsid() const126 const std::string& TokenService::GetLsid() const {
127   return credentials_.lsid;
128 }
129 
StartFetchingTokens()130 void TokenService::StartFetchingTokens() {
131   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
132   DCHECK(AreCredentialsValid());
133   for (int i = 0; i < kNumServices; i++) {
134     fetchers_[i]->StartIssueAuthToken(credentials_.sid,
135                                       credentials_.lsid,
136                                       kServices[i]);
137   }
138 }
139 
140 // Services dependent on a token will check if a token is available.
141 // If it isn't, they'll go to sleep until they get a token event.
HasTokenForService(const char * const service) const142 bool TokenService::HasTokenForService(const char* const service) const {
143   return token_map_.count(service) > 0;
144 }
145 
GetTokenForService(const char * const service) const146 const std::string& TokenService::GetTokenForService(
147     const char* const service) const {
148 
149   if (token_map_.count(service) > 0) {
150     // Note map[key] is not const.
151     return (*token_map_.find(service)).second;
152   }
153   return EmptyString();
154 }
155 
156 // Note that this can fire twice or more for any given service.
157 // It can fire once from the DB read, and then once from the initial
158 // fetcher. Future fetches can cause more notification firings.
159 // The DB read will not however fire a notification if the fetcher
160 // returned first. So it's always safe to use the latest notification.
FireTokenAvailableNotification(const std::string & service,const std::string & auth_token)161 void TokenService::FireTokenAvailableNotification(
162     const std::string& service,
163     const std::string& auth_token) {
164 
165   TokenAvailableDetails details(service, auth_token);
166   NotificationService::current()->Notify(
167       NotificationType::TOKEN_AVAILABLE,
168       Source<TokenService>(this),
169       Details<const TokenAvailableDetails>(&details));
170 }
171 
FireTokenRequestFailedNotification(const std::string & service,const GoogleServiceAuthError & error)172 void TokenService::FireTokenRequestFailedNotification(
173     const std::string& service,
174     const GoogleServiceAuthError& error) {
175 
176   TokenRequestFailedDetails details(service, error);
177   NotificationService::current()->Notify(
178       NotificationType::TOKEN_REQUEST_FAILED,
179       Source<TokenService>(this),
180       Details<const TokenRequestFailedDetails>(&details));
181 }
182 
IssueAuthTokenForTest(const std::string & service,const std::string & auth_token)183 void TokenService::IssueAuthTokenForTest(const std::string& service,
184                                          const std::string& auth_token) {
185   token_map_[service] = auth_token;
186   FireTokenAvailableNotification(service, auth_token);
187 }
188 
OnIssueAuthTokenSuccess(const std::string & service,const std::string & auth_token)189 void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
190                                            const std::string& auth_token) {
191   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
192   VLOG(1) << "Got an authorization token for " << service;
193   token_map_[service] = auth_token;
194   FireTokenAvailableNotification(service, auth_token);
195   SaveAuthTokenToDB(service, auth_token);
196 }
197 
OnIssueAuthTokenFailure(const std::string & service,const GoogleServiceAuthError & error)198 void TokenService::OnIssueAuthTokenFailure(const std::string& service,
199     const GoogleServiceAuthError& error) {
200   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
201   LOG(WARNING) << "Auth token issuing failed for service:" << service;
202   FireTokenRequestFailedNotification(service, error);
203 }
204 
OnWebDataServiceRequestDone(WebDataService::Handle h,const WDTypedResult * result)205 void TokenService::OnWebDataServiceRequestDone(WebDataService::Handle h,
206                                                const WDTypedResult* result) {
207   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208   DCHECK(token_loading_query_);
209   token_loading_query_ = 0;
210 
211   // If the fetch failed, there will be no result. In that case, we just don't
212   // load any tokens at all from the DB.
213   if (result) {
214     DCHECK(result->GetType() == TOKEN_RESULT);
215     const WDResult<std::map<std::string, std::string> > * token_result =
216         static_cast<const WDResult<std::map<std::string, std::string> > * > (
217             result);
218     LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
219   }
220 
221   NotificationService::current()->Notify(
222       NotificationType::TOKEN_LOADING_FINISHED,
223       Source<TokenService>(this),
224       NotificationService::NoDetails());
225 }
226 
227 // Load tokens from the db_token map into the in memory token map.
LoadTokensIntoMemory(const std::map<std::string,std::string> & db_tokens,std::map<std::string,std::string> * in_memory_tokens)228 void TokenService::LoadTokensIntoMemory(
229     const std::map<std::string, std::string>& db_tokens,
230     std::map<std::string, std::string>* in_memory_tokens) {
231 
232   for (int i = 0; i < kNumServices; i++) {
233     // OnIssueAuthTokenSuccess should come from the same thread.
234     // If a token is already present in the map, it could only have
235     // come from a DB read or from IssueAuthToken. Since we should never
236     // fetch from the DB twice in a browser session, it must be from
237     // OnIssueAuthTokenSuccess, which is a live fetcher.
238     //
239     // Network fetched tokens take priority over DB tokens, so exclude tokens
240     // which have already been loaded by the fetcher.
241     if (!in_memory_tokens->count(kServices[i]) &&
242         db_tokens.count(kServices[i])) {
243       std::string db_token = db_tokens.find(kServices[i])->second;
244       if (!db_token.empty()) {
245         VLOG(1) << "Loading " << kServices[i] << "token from DB: " << db_token;
246         (*in_memory_tokens)[kServices[i]] = db_token;
247         FireTokenAvailableNotification(kServices[i], db_token);
248         // Failures are only for network errors.
249       }
250     }
251   }
252 }
253 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)254 void TokenService::Observe(NotificationType type,
255                            const NotificationSource& source,
256                            const NotificationDetails& details) {
257   DCHECK(type == NotificationType::TOKEN_UPDATED);
258   TokenAvailableDetails* tok_details =
259       Details<TokenAvailableDetails>(details).ptr();
260   OnIssueAuthTokenSuccess(tok_details->service(), tok_details->token());
261 }
262