- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / token_service.cc
1 // Copyright 2013 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/signin/token_service.h"
6
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_util.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/signin/signin_manager.h"
14 #include "chrome/browser/signin/signin_manager_factory.h"
15 #include "chrome/browser/webdata/token_web_data.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/pref_names.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_source.h"
21 #include "google_apis/gaia/gaia_auth_fetcher.h"
22 #include "google_apis/gaia/gaia_constants.h"
23 #include "net/url_request/url_request_context_getter.h"
24
25 #if defined(OS_CHROMEOS)
26 #include "base/metrics/histogram.h"
27 #endif
28
29 using content::BrowserThread;
30 using namespace signin_internals_util;
31
32 namespace {
33
34 // List of services that are capable of ClientLogin-based authentication.
35 const char* kServices[] = {
36   GaiaConstants::kSyncService
37 };
38
39 }  // namespace
40
41
42 TokenService::TokenService()
43     : profile_(NULL),
44       token_loading_query_(0),
45       tokens_loaded_(false) {
46   // Allow constructor to be called outside the UI thread, so it can be mocked
47   // out for unit tests.
48
49   COMPILE_ASSERT(arraysize(kServices) == arraysize(fetchers_),
50                  kServices_and_fetchers_dont_have_same_size);
51 }
52
53 TokenService::~TokenService() {
54 }
55
56 void TokenService::Shutdown() {
57   if (!source_.empty()) {
58     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59     ResetCredentialsInMemory();
60   }
61   token_web_data_ = NULL;
62 }
63
64 void TokenService::Initialize(const char* const source,
65                               Profile* profile) {
66   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
67   if (!source_.empty()) {
68     // Already initialized.
69     return;
70   }
71   DCHECK(!profile_);
72   profile_ = profile;
73   getter_ = profile->GetRequestContext();
74   // Since the user can create a bookmark in incognito, sync may be running.
75   // Thus we have to go for explicit access.
76   token_web_data_ = TokenWebData::FromBrowserContext(profile);
77   source_ = std::string(source);
78
79   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
80   // Allow the token service to be cleared from the command line. We rely on
81   // SigninManager::Initialize() being called to clear out the
82   // kGoogleServicesUsername pref before we call EraseTokensFromDB() as
83   // otherwise the system would be in an invalid state.
84   if (cmd_line->HasSwitch(switches::kClearTokenService))
85     EraseTokensFromDB();
86
87   // Allow a token to be injected from the command line.
88   if (cmd_line->HasSwitch(switches::kSetToken)) {
89     std::string value = cmd_line->GetSwitchValueASCII(switches::kSetToken);
90     int separator = value.find(':');
91     std::string service = value.substr(0, separator);
92     std::string token = value.substr(separator + 1);
93     token_map_[service] = token;
94     SaveAuthTokenToDB(service, token);
95   }
96 }
97
98 void TokenService::AddAuthTokenManually(const std::string& service,
99                                         const std::string& auth_token) {
100   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
101   VLOG(1) << "Got an authorization token for " << service;
102   token_map_[service] = auth_token;
103   FireTokenAvailableNotification(service, auth_token);
104   SaveAuthTokenToDB(service, auth_token);
105 }
106
107
108 void TokenService::ResetCredentialsInMemory() {
109   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110
111   // Terminate any running fetchers. Callbacks will not return.
112   for (size_t i = 0; i < arraysize(kServices); ++i) {
113     fetchers_[i].reset();
114   }
115
116   // Cancel pending loads. Callbacks will not return.
117   if (token_loading_query_) {
118     token_web_data_->CancelRequest(token_loading_query_);
119     token_loading_query_ = 0;
120   }
121
122   tokens_loaded_ = false;
123   token_map_.clear();
124   credentials_ = GaiaAuthConsumer::ClientLoginResult();
125 }
126
127 void TokenService::UpdateCredentials(
128     const GaiaAuthConsumer::ClientLoginResult& credentials) {
129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130   credentials_ = credentials;
131
132   SaveAuthTokenToDB(GaiaConstants::kGaiaLsid, credentials.lsid);
133   SaveAuthTokenToDB(GaiaConstants::kGaiaSid, credentials.sid);
134
135   // Cancel any currently running requests.
136   for (size_t i = 0; i < arraysize(kServices); i++) {
137     fetchers_[i].reset();
138   }
139 }
140
141 void TokenService::UpdateCredentialsWithOAuth2(
142     const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
143   SaveOAuth2Credentials(oauth2_tokens);
144 }
145
146 void TokenService::LoadTokensFromDB() {
147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
148   if (token_web_data_.get())
149     token_loading_query_ = token_web_data_->GetAllTokens(this);
150 }
151
152 void TokenService::SaveAuthTokenToDB(const std::string& service,
153                                      const std::string& auth_token) {
154   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
155   if (token_web_data_.get())
156     token_web_data_->SetTokenForService(service, auth_token);
157 }
158
159 void TokenService::EraseTokensFromDB() {
160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161   if (token_web_data_.get())
162     token_web_data_->RemoveAllTokens();
163
164   content::NotificationService::current()->Notify(
165       chrome::NOTIFICATION_TOKENS_CLEARED,
166       content::Source<TokenService>(this),
167       content::NotificationService::NoDetails());
168
169 }
170
171 bool TokenService::TokensLoadedFromDB() const {
172   return tokens_loaded_;
173 }
174
175 // static
176 int TokenService::GetServiceIndex(const std::string& service) {
177   for (size_t i = 0; i < arraysize(kServices); ++i) {
178     if (kServices[i] == service)
179       return i;
180   }
181   return -1;
182 }
183
184 bool TokenService::AreCredentialsValid() const {
185   return !credentials_.lsid.empty() && !credentials_.sid.empty();
186 }
187
188 void TokenService::StartFetchingTokens() {
189   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
190   DCHECK(AreCredentialsValid());
191   for (size_t i = 0; i < arraysize(kServices); i++) {
192     fetchers_[i].reset(new GaiaAuthFetcher(this, source_, getter_.get()));
193     fetchers_[i]->StartIssueAuthToken(
194         credentials_.sid, credentials_.lsid, kServices[i]);
195   }
196 }
197
198 // Services dependent on a token will check if a token is available.
199 // If it isn't, they'll go to sleep until they get a token event.
200 bool TokenService::HasTokenForService(const char* service) const {
201   return token_map_.count(service) > 0;
202 }
203
204 const std::string& TokenService::GetTokenForService(
205     const char* const service) const {
206
207   if (token_map_.count(service) > 0) {
208     // Note map[key] is not const.
209     return (*token_map_.find(service)).second;
210   }
211   return EmptyString();
212 }
213
214 bool TokenService::HasOAuthLoginToken() const {
215   return HasTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
216 }
217
218 const std::string& TokenService::GetOAuth2LoginRefreshToken() const {
219   return GetTokenForService(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
220 }
221
222 // static
223 void TokenService::GetServiceNames(std::vector<std::string>* names) {
224   names->resize(arraysize(kServices));
225   std::copy(kServices, kServices + arraysize(kServices), names->begin());
226 }
227
228 // Note that this can fire twice or more for any given service.
229 // It can fire once from the DB read, and then once from the initial
230 // fetcher. Future fetches can cause more notification firings.
231 // The DB read will not however fire a notification if the fetcher
232 // returned first. So it's always safe to use the latest notification.
233 void TokenService::FireTokenAvailableNotification(
234     const std::string& service,
235     const std::string& auth_token) {
236
237   TokenAvailableDetails details(service, auth_token);
238   content::NotificationService::current()->Notify(
239       chrome::NOTIFICATION_TOKEN_AVAILABLE,
240       content::Source<TokenService>(this),
241       content::Details<const TokenAvailableDetails>(&details));
242 }
243
244 void TokenService::FireTokenRequestFailedNotification(
245     const std::string& service,
246     const GoogleServiceAuthError& error) {
247
248 #if defined(OS_CHROMEOS)
249   std::string metric = "TokenService.TokenRequestFailed." + service;
250   // We can't use the UMA_HISTOGRAM_ENUMERATION here since the macro creates
251   // a static histogram in the function - locking us to one metric name. Since
252   // the metric name can be "TokenService.TokenRequestFailed." + one of four
253   // different values, we need to create the histogram ourselves and add the
254   // sample.
255   base::HistogramBase* histogram =
256       base::LinearHistogram::FactoryGet(
257           metric,
258           1,
259           GoogleServiceAuthError::NUM_STATES,
260           GoogleServiceAuthError::NUM_STATES + 1,
261           base::HistogramBase::kUmaTargetedHistogramFlag);
262   histogram->Add(error.state());
263 #endif
264
265   TokenRequestFailedDetails details(service, error);
266   content::NotificationService::current()->Notify(
267       chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
268       content::Source<TokenService>(this),
269       content::Details<const TokenRequestFailedDetails>(&details));
270 }
271
272 void TokenService::IssueAuthTokenForTest(const std::string& service,
273                                          const std::string& auth_token) {
274   token_map_[service] = auth_token;
275   FireTokenAvailableNotification(service, auth_token);
276 }
277
278 void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
279                                            const std::string& auth_token) {
280   AddAuthTokenManually(service, auth_token);
281 }
282
283 void TokenService::OnIssueAuthTokenFailure(const std::string& service,
284     const GoogleServiceAuthError& error) {
285   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
286   LOG(WARNING) << "Auth token issuing failed for service:" << service
287                << ", error: " << error.ToString();
288   FireTokenRequestFailedNotification(service, error);
289 }
290
291 void TokenService::OnClientOAuthSuccess(const ClientOAuthResult& result) {
292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
293   VLOG(1) << "Got OAuth2 login token pair";
294   SaveOAuth2Credentials(result);
295 }
296
297 void TokenService::SaveOAuth2Credentials(const ClientOAuthResult& result) {
298   token_map_[GaiaConstants::kGaiaOAuth2LoginRefreshToken] =
299       result.refresh_token;
300   SaveAuthTokenToDB(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
301       result.refresh_token);
302   // We don't save expiration information for now.
303
304   FireTokenAvailableNotification(GaiaConstants::kGaiaOAuth2LoginRefreshToken,
305       result.refresh_token);
306 }
307
308 void TokenService::OnClientOAuthFailure(
309    const GoogleServiceAuthError& error) {
310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311   LOG(WARNING) << "OAuth2 login token pair fetch failed: " << error.ToString();
312   FireTokenRequestFailedNotification(
313       GaiaConstants::kGaiaOAuth2LoginRefreshToken, error);
314 }
315
316 void TokenService::OnWebDataServiceRequestDone(WebDataServiceBase::Handle h,
317                                                const WDTypedResult* result) {
318   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319   DCHECK(token_loading_query_);
320   token_loading_query_ = 0;
321
322   // If the fetch failed, there will be no result. In that case, we just don't
323   // load any tokens at all from the DB.
324   if (result) {
325     DCHECK(result->GetType() == TOKEN_RESULT);
326     const WDResult<std::map<std::string, std::string> > * token_result =
327         static_cast<const WDResult<std::map<std::string, std::string> > * > (
328             result);
329     LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
330   }
331
332   tokens_loaded_ = true;
333   content::NotificationService::current()->Notify(
334       chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
335       content::Source<TokenService>(this),
336       content::NotificationService::NoDetails());
337 }
338
339 // Load tokens from the db_token map into the in memory token map.
340 void TokenService::LoadTokensIntoMemory(
341     const std::map<std::string, std::string>& db_tokens,
342     std::map<std::string, std::string>* in_memory_tokens) {
343   // Ensure that there are no active fetchers at the time we first load
344   // tokens from the DB into memory.
345   for (size_t i = 0; i < arraysize(kServices); ++i) {
346     DCHECK(NULL == fetchers_[i].get());
347   }
348
349   for (size_t i = 0; i < arraysize(kServices); i++) {
350     LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens, kServices[i]);
351   }
352   LoadSingleTokenIntoMemory(db_tokens, in_memory_tokens,
353       GaiaConstants::kGaiaOAuth2LoginRefreshToken);
354
355   if (credentials_.lsid.empty() && credentials_.sid.empty()) {
356     // Look for GAIA SID and LSID tokens.  If we have both, and the current
357     // crendentials are empty, update the credentials.
358     std::string lsid;
359     std::string sid;
360
361     if (db_tokens.count(GaiaConstants::kGaiaLsid) > 0)
362       lsid = db_tokens.find(GaiaConstants::kGaiaLsid)->second;
363
364     if (db_tokens.count(GaiaConstants::kGaiaSid) > 0)
365       sid = db_tokens.find(GaiaConstants::kGaiaSid)->second;
366
367     if (!lsid.empty() && !sid.empty()) {
368       credentials_ = GaiaAuthConsumer::ClientLoginResult(sid,
369                                                          lsid,
370                                                          std::string(),
371                                                          std::string());
372     }
373   }
374 }
375
376 void TokenService::LoadSingleTokenIntoMemory(
377     const std::map<std::string, std::string>& db_tokens,
378     std::map<std::string, std::string>* in_memory_tokens,
379     const std::string& service) {
380   // OnIssueAuthTokenSuccess should come from the same thread.
381   // If a token is already present in the map, it could only have
382   // come from a DB read or from IssueAuthToken. Since we should never
383   // fetch from the DB twice in a browser session, it must be from
384   // OnIssueAuthTokenSuccess, which is a live fetcher.
385   //
386   // Network fetched tokens take priority over DB tokens, so exclude tokens
387   // which have already been loaded by the fetcher.
388   if (!in_memory_tokens->count(service) && db_tokens.count(service)) {
389     std::string db_token = db_tokens.find(service)->second;
390     if (!db_token.empty()) {
391       VLOG(1) << "Loading " << service << " token from DB";
392       (*in_memory_tokens)[service] = db_token;
393       FireTokenAvailableNotification(service, db_token);
394       // Failures are only for network errors.
395     }
396   }
397 }
398
399 void TokenService::AddSigninDiagnosticsObserver(
400     SigninDiagnosticsObserver* observer) {
401   signin_diagnostics_observers_.AddObserver(observer);
402 }
403
404 void TokenService::RemoveSigninDiagnosticsObserver(
405     SigninDiagnosticsObserver* observer) {
406   signin_diagnostics_observers_.RemoveObserver(observer);
407 }