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