Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / signin / core / browser / mutable_profile_oauth2_token_service.cc
1 // Copyright 2014 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 "components/signin/core/browser/mutable_profile_oauth2_token_service.h"
6
7 #include "components/signin/core/browser/signin_client.h"
8 #include "components/signin/core/browser/signin_metrics.h"
9 #include "components/signin/core/browser/webdata/token_web_data.h"
10 #include "components/webdata/common/web_data_service_base.h"
11 #include "google_apis/gaia/gaia_auth_fetcher.h"
12 #include "google_apis/gaia/gaia_constants.h"
13 #include "google_apis/gaia/google_service_auth_error.h"
14 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
15 #include "net/url_request/url_request_context_getter.h"
16
17 namespace {
18
19 const char kAccountIdPrefix[] = "AccountId-";
20 const size_t kAccountIdPrefixLength = 10;
21
22 std::string ApplyAccountIdPrefix(const std::string& account_id) {
23   return kAccountIdPrefix + account_id;
24 }
25
26 bool IsLegacyRefreshTokenId(const std::string& service_id) {
27   return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
28 }
29
30 bool IsLegacyServiceId(const std::string& account_id) {
31   return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
32 }
33
34 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
35   return prefixed_account_id.substr(kAccountIdPrefixLength);
36 }
37
38 }  // namespace
39
40 // This class sends a request to GAIA to revoke the given refresh token from
41 // the server.  This is a best effort attempt only.  This class deletes itself
42 // when done sucessfully or otherwise.
43 class MutableProfileOAuth2TokenService::RevokeServerRefreshToken
44     : public GaiaAuthConsumer {
45  public:
46   RevokeServerRefreshToken(MutableProfileOAuth2TokenService* token_service,
47                            const std::string& account_id);
48   virtual ~RevokeServerRefreshToken();
49
50  private:
51   // GaiaAuthConsumer overrides:
52   virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;
53
54   MutableProfileOAuth2TokenService* token_service_;
55   GaiaAuthFetcher fetcher_;
56
57   DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
58 };
59
60 MutableProfileOAuth2TokenService::
61     RevokeServerRefreshToken::RevokeServerRefreshToken(
62     MutableProfileOAuth2TokenService* token_service,
63     const std::string& refresh_token)
64     : token_service_(token_service),
65       fetcher_(this, GaiaConstants::kChromeSource,
66                token_service_->GetRequestContext()) {
67   fetcher_.StartRevokeOAuth2Token(refresh_token);
68 }
69
70 MutableProfileOAuth2TokenService::
71     RevokeServerRefreshToken::~RevokeServerRefreshToken() {}
72
73 void MutableProfileOAuth2TokenService::
74     RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() {
75   // |this| pointer will be deleted when removed from the vector, so don't
76   // access any members after call to erase().
77   token_service_->server_revokes_.erase(
78       std::find(token_service_->server_revokes_.begin(),
79                 token_service_->server_revokes_.end(),
80                 this));
81 }
82
83 MutableProfileOAuth2TokenService::AccountInfo::AccountInfo(
84     ProfileOAuth2TokenService* token_service,
85     const std::string& account_id,
86     const std::string& refresh_token)
87   : token_service_(token_service),
88     account_id_(account_id),
89     refresh_token_(refresh_token),
90     last_auth_error_(GoogleServiceAuthError::NONE) {
91   DCHECK(token_service_);
92   DCHECK(!account_id_.empty());
93   token_service_->signin_error_controller()->AddProvider(this);
94 }
95
96 MutableProfileOAuth2TokenService::AccountInfo::~AccountInfo() {
97   token_service_->signin_error_controller()->RemoveProvider(this);
98 }
99
100 void MutableProfileOAuth2TokenService::AccountInfo::SetLastAuthError(
101     const GoogleServiceAuthError& error) {
102   if (error.state() != last_auth_error_.state()) {
103     last_auth_error_ = error;
104     token_service_->signin_error_controller()->AuthStatusChanged();
105   }
106 }
107
108 std::string
109 MutableProfileOAuth2TokenService::AccountInfo::GetAccountId() const {
110   return account_id_;
111 }
112
113 std::string
114 MutableProfileOAuth2TokenService::AccountInfo::GetUsername() const {
115   // TODO(rogerta): when |account_id| becomes the obfuscated gaia id, this
116   // will need to be changed.
117   return account_id_;
118 }
119
120 GoogleServiceAuthError
121 MutableProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const {
122   return last_auth_error_;
123 }
124
125 MutableProfileOAuth2TokenService::MutableProfileOAuth2TokenService()
126     : web_data_service_request_(0)  {
127 }
128
129 MutableProfileOAuth2TokenService::~MutableProfileOAuth2TokenService() {
130   DCHECK(server_revokes_.empty());
131 }
132
133 void MutableProfileOAuth2TokenService::Shutdown() {
134   server_revokes_.clear();
135   CancelWebTokenFetch();
136   CancelAllRequests();
137   refresh_tokens_.clear();
138
139   ProfileOAuth2TokenService::Shutdown();
140 }
141
142 bool MutableProfileOAuth2TokenService::RefreshTokenIsAvailable(
143     const std::string& account_id) const {
144   return !GetRefreshToken(account_id).empty();
145 }
146
147 std::string MutableProfileOAuth2TokenService::GetRefreshToken(
148     const std::string& account_id) const {
149   AccountInfoMap::const_iterator iter = refresh_tokens_.find(account_id);
150   if (iter != refresh_tokens_.end())
151     return iter->second->refresh_token();
152   return std::string();
153 }
154
155 OAuth2AccessTokenFetcher*
156 MutableProfileOAuth2TokenService::CreateAccessTokenFetcher(
157     const std::string& account_id,
158     net::URLRequestContextGetter* getter,
159     OAuth2AccessTokenConsumer* consumer) {
160   std::string refresh_token = GetRefreshToken(account_id);
161   DCHECK(!refresh_token.empty());
162   return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
163 }
164
165 net::URLRequestContextGetter*
166 MutableProfileOAuth2TokenService::GetRequestContext() {
167   return client()->GetURLRequestContext();
168 }
169
170 void MutableProfileOAuth2TokenService::LoadCredentials(
171     const std::string& primary_account_id) {
172   DCHECK(!primary_account_id.empty());
173   DCHECK(loading_primary_account_id_.empty());
174   DCHECK_EQ(0, web_data_service_request_);
175
176   CancelAllRequests();
177   refresh_tokens().clear();
178   loading_primary_account_id_ = primary_account_id;
179   scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
180   if (token_web_data.get())
181     web_data_service_request_ = token_web_data->GetAllTokens(this);
182 }
183
184 void MutableProfileOAuth2TokenService::OnWebDataServiceRequestDone(
185     WebDataServiceBase::Handle handle,
186     const WDTypedResult* result) {
187   DCHECK_EQ(web_data_service_request_, handle);
188   web_data_service_request_ = 0;
189
190   if (result) {
191     DCHECK(result->GetType() == TOKEN_RESULT);
192     const WDResult<std::map<std::string, std::string> > * token_result =
193         static_cast<const WDResult<std::map<std::string, std::string> > * > (
194             result);
195     LoadAllCredentialsIntoMemory(token_result->GetValue());
196   }
197
198   // Make sure that we have an entry for |loading_primary_account_id_| in the
199   // map.  The entry could be missing if there is a corruption in the token DB
200   // while this profile is connected to an account.
201   DCHECK(!loading_primary_account_id_.empty());
202   if (refresh_tokens().count(loading_primary_account_id_) == 0) {
203     refresh_tokens()[loading_primary_account_id_].reset(
204         new AccountInfo(this, loading_primary_account_id_, std::string()));
205   }
206
207   // If we don't have a refresh token for a known account, signal an error.
208   for (AccountInfoMap::const_iterator i = refresh_tokens_.begin();
209        i != refresh_tokens_.end(); ++i) {
210     if (!RefreshTokenIsAvailable(i->first)) {
211       UpdateAuthError(
212           i->first,
213           GoogleServiceAuthError(
214               GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
215       break;
216     }
217   }
218
219   loading_primary_account_id_.clear();
220 }
221
222 void MutableProfileOAuth2TokenService::LoadAllCredentialsIntoMemory(
223     const std::map<std::string, std::string>& db_tokens) {
224   std::string old_login_token;
225
226   {
227     ScopedBacthChange batch(this);
228
229     for (std::map<std::string, std::string>::const_iterator iter =
230              db_tokens.begin();
231          iter != db_tokens.end();
232          ++iter) {
233       std::string prefixed_account_id = iter->first;
234       std::string refresh_token = iter->second;
235
236       if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
237         old_login_token = refresh_token;
238
239       if (IsLegacyServiceId(prefixed_account_id)) {
240         scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
241         if (token_web_data.get())
242           token_web_data->RemoveTokenForService(prefixed_account_id);
243       } else {
244         DCHECK(!refresh_token.empty());
245         std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
246         refresh_tokens()[account_id].reset(
247             new AccountInfo(this, account_id, refresh_token));
248         FireRefreshTokenAvailable(account_id);
249         // TODO(fgorski): Notify diagnostic observers.
250       }
251     }
252
253     if (!old_login_token.empty()) {
254       DCHECK(!loading_primary_account_id_.empty());
255       if (refresh_tokens().count(loading_primary_account_id_) == 0)
256         UpdateCredentials(loading_primary_account_id_, old_login_token);
257     }
258   }
259
260   FireRefreshTokensLoaded();
261 }
262
263 void MutableProfileOAuth2TokenService::UpdateAuthError(
264     const std::string& account_id,
265     const GoogleServiceAuthError& error) {
266   // Do not report connection errors as these are not actually auth errors.
267   // We also want to avoid masking a "real" auth error just because we
268   // subsequently get a transient network error.
269   if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
270       error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE)
271     return;
272
273 #if defined(OS_IOS)
274   // ProfileOauth2TokenService does not manage the refresh tokens on iOS - the
275   // account info on iOS is only used to manage the authentication error state.
276   // Simply add an account info entry with empty refresh token if none exists.
277   if (refresh_tokens_.count(account_id) == 0) {
278       refresh_tokens_[account_id].reset(
279           new AccountInfo(this, account_id, std::string()));
280   }
281 #endif
282
283   if (refresh_tokens_.count(account_id) == 0) {
284     // This could happen if the preferences have been corrupted (see
285     // http://crbug.com/321370). In a Debug build that would be a bug, but in a
286     // Release build we want to deal with it gracefully.
287     NOTREACHED();
288     return;
289   }
290   refresh_tokens_[account_id]->SetLastAuthError(error);
291 }
292
293 std::vector<std::string> MutableProfileOAuth2TokenService::GetAccounts() {
294   std::vector<std::string> account_ids;
295   for (AccountInfoMap::const_iterator iter = refresh_tokens_.begin();
296            iter != refresh_tokens_.end(); ++iter) {
297     account_ids.push_back(iter->first);
298   }
299   return account_ids;
300 }
301
302 void MutableProfileOAuth2TokenService::UpdateCredentials(
303     const std::string& account_id,
304     const std::string& refresh_token) {
305   DCHECK(thread_checker_.CalledOnValidThread());
306   DCHECK(!account_id.empty());
307   DCHECK(!refresh_token.empty());
308
309   signin_metrics::LogSigninAddAccount();
310
311   bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
312   if (!refresh_token_present ||
313       refresh_tokens_[account_id]->refresh_token() != refresh_token) {
314     ScopedBacthChange batch(this);
315
316     // If token present, and different from the new one, cancel its requests,
317     // and clear the entries in cache related to that account.
318     if (refresh_token_present) {
319       std::string revoke_reason = refresh_token_present ? "token differs" :
320                                                           "token is missing";
321       LOG(WARNING) << "Revoking refresh token on server. "
322                    << "Reason: token update, " << revoke_reason;
323       RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
324       CancelRequestsForAccount(account_id);
325       ClearCacheForAccount(account_id);
326       refresh_tokens_[account_id]->set_refresh_token(refresh_token);
327     } else {
328       refresh_tokens_[account_id].reset(
329           new AccountInfo(this, account_id, refresh_token));
330     }
331
332     // Save the token in memory and in persistent store.
333     PersistCredentials(account_id, refresh_token);
334
335     UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
336     FireRefreshTokenAvailable(account_id);
337   }
338 }
339
340 void MutableProfileOAuth2TokenService::RevokeCredentials(
341     const std::string& account_id) {
342   DCHECK(thread_checker_.CalledOnValidThread());
343
344   if (refresh_tokens_.count(account_id) > 0) {
345     ScopedBacthChange batch(this);
346     RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
347     CancelRequestsForAccount(account_id);
348     ClearCacheForAccount(account_id);
349     refresh_tokens_.erase(account_id);
350     ClearPersistedCredentials(account_id);
351     FireRefreshTokenRevoked(account_id);
352   }
353 }
354
355 void MutableProfileOAuth2TokenService::PersistCredentials(
356     const std::string& account_id,
357     const std::string& refresh_token) {
358   scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
359   if (token_web_data.get()) {
360     token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
361                                        refresh_token);
362   }
363 }
364
365 void MutableProfileOAuth2TokenService::ClearPersistedCredentials(
366     const std::string& account_id) {
367   scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
368   if (token_web_data.get())
369     token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
370 }
371
372 void MutableProfileOAuth2TokenService::RevokeAllCredentials() {
373   if (!client()->CanRevokeCredentials())
374     return;
375   DCHECK(thread_checker_.CalledOnValidThread());
376
377   ScopedBacthChange batch(this);
378
379   CancelWebTokenFetch();
380   CancelAllRequests();
381   ClearCache();
382   AccountInfoMap tokens = refresh_tokens_;
383   for (AccountInfoMap::iterator i = tokens.begin(); i != tokens.end(); ++i)
384     RevokeCredentials(i->first);
385
386   DCHECK_EQ(0u, refresh_tokens_.size());
387 }
388
389 void MutableProfileOAuth2TokenService::RevokeCredentialsOnServer(
390     const std::string& refresh_token) {
391   // Keep track or all server revoke requests.  This way they can be deleted
392   // before the token service is shutdown and won't outlive the profile.
393   server_revokes_.push_back(
394       new RevokeServerRefreshToken(this, refresh_token));
395 }
396
397 void MutableProfileOAuth2TokenService::CancelWebTokenFetch() {
398   if (web_data_service_request_ != 0) {
399     scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
400     DCHECK(token_web_data.get());
401     token_web_data->CancelRequest(web_data_service_request_);
402     web_data_service_request_  = 0;
403   }
404 }