Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: chrome/browser/signin/account_reconcilor.cc

Issue 219933002: Componentize AccountReconcilor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ChromeOS fix Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/account_reconcilor.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/json/json_reader.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/signin/signin_oauth_helper.h"
20 #include "components/signin/core/browser/profile_oauth2_token_service.h"
21 #include "components/signin/core/browser/signin_client.h"
22 #include "google_apis/gaia/gaia_auth_fetcher.h"
23 #include "google_apis/gaia/gaia_auth_util.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "google_apis/gaia/gaia_oauth_client.h"
26 #include "google_apis/gaia/gaia_urls.h"
27
28 // Fetches a refresh token from the given session in the GAIA cookie. This is
29 // a best effort only. If it should fail, another reconcile action will occur
30 // shortly anyway.
31 class AccountReconcilor::RefreshTokenFetcher
32 : public SigninOAuthHelper,
33 public SigninOAuthHelper::Consumer {
34 public:
35 RefreshTokenFetcher(AccountReconcilor* reconcilor,
36 const std::string& account_id,
37 int session_index);
38 virtual ~RefreshTokenFetcher() {}
39
40 private:
41 // Overridden from GaiaAuthConsumer:
42 virtual void OnSigninOAuthInformationAvailable(
43 const std::string& email,
44 const std::string& display_email,
45 const std::string& refresh_token) OVERRIDE;
46
47 // Called when an error occurs while getting the information.
48 virtual void OnSigninOAuthInformationFailure(
49 const GoogleServiceAuthError& error) OVERRIDE;
50
51 AccountReconcilor* reconcilor_;
52 const std::string account_id_;
53 int session_index_;
54
55 DISALLOW_COPY_AND_ASSIGN(RefreshTokenFetcher);
56 };
57
58 AccountReconcilor::RefreshTokenFetcher::RefreshTokenFetcher(
59 AccountReconcilor* reconcilor,
60 const std::string& account_id,
61 int session_index)
62 : SigninOAuthHelper(reconcilor->profile()->GetRequestContext(),
63 base::IntToString(session_index), this),
64 reconcilor_(reconcilor),
65 account_id_(account_id),
66 session_index_(session_index) {
67 DCHECK(reconcilor_);
68 DCHECK(!account_id.empty());
69 }
70
71 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationAvailable(
72 const std::string& email,
73 const std::string& display_email,
74 const std::string& refresh_token) {
75 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationAvailable:"
76 << " account=" << account_id_
77 << " email=" << email
78 << " displayEmail=" << display_email;
79
80 // TODO(rogerta): because of the problem with email vs displayEmail and
81 // emails that have been canonicalized, the argument |email| is used here
82 // to make sure the correct string is used when calling the token service.
83 // This will be cleaned up when chrome moves to using gaia obfuscated id.
84 reconcilor_->HandleRefreshTokenFetched(email, refresh_token);
85 }
86
87 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationFailure(
88 const GoogleServiceAuthError& error) {
89 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationFailure:"
90 << " account=" << account_id_
91 << " session_index=" << session_index_;
92 reconcilor_->HandleRefreshTokenFetched(account_id_, std::string());
93 }
94
95
96 bool AccountReconcilor::EmailLessFunc::operator()(const std::string& s1,
97 const std::string& s2) const {
98 return gaia::CanonicalizeEmail(s1) < gaia::CanonicalizeEmail(s2);
99 }
100
101 class AccountReconcilor::UserIdFetcher
102 : public gaia::GaiaOAuthClient::Delegate {
103 public:
104 UserIdFetcher(AccountReconcilor* reconcilor,
105 const std::string& access_token,
106 const std::string& account_id);
107
108 // Returns the scopes needed by the UserIdFetcher.
109 static OAuth2TokenService::ScopeSet GetScopes();
110
111 private:
112 // Overriden from gaia::GaiaOAuthClient::Delegate.
113 virtual void OnGetUserIdResponse(const std::string& user_id) OVERRIDE;
114 virtual void OnOAuthError() OVERRIDE;
115 virtual void OnNetworkError(int response_code) OVERRIDE;
116
117 AccountReconcilor* const reconcilor_;
118 const std::string account_id_;
119 const std::string access_token_;
120 gaia::GaiaOAuthClient gaia_auth_client_;
121
122 DISALLOW_COPY_AND_ASSIGN(UserIdFetcher);
123 };
124
125 AccountReconcilor::UserIdFetcher::UserIdFetcher(AccountReconcilor* reconcilor,
126 const std::string& access_token,
127 const std::string& account_id)
128 : reconcilor_(reconcilor),
129 account_id_(account_id),
130 access_token_(access_token),
131 gaia_auth_client_(reconcilor_->profile()->GetRequestContext()) {
132 DCHECK(reconcilor_);
133 DCHECK(!account_id_.empty());
134
135 const int kMaxRetries = 5;
136 gaia_auth_client_.GetUserId(access_token_, kMaxRetries, this);
137 }
138
139 // static
140 OAuth2TokenService::ScopeSet AccountReconcilor::UserIdFetcher::GetScopes() {
141 OAuth2TokenService::ScopeSet scopes;
142 scopes.insert("https://www.googleapis.com/auth/userinfo.profile");
143 return scopes;
144 }
145
146 void AccountReconcilor::UserIdFetcher::OnGetUserIdResponse(
147 const std::string& user_id) {
148 VLOG(1) << "AccountReconcilor::OnGetUserIdResponse: " << account_id_;
149
150 // HandleSuccessfulAccountIdCheck() may delete |this|, so call it last.
151 reconcilor_->HandleSuccessfulAccountIdCheck(account_id_);
152 }
153
154 void AccountReconcilor::UserIdFetcher::OnOAuthError() {
155 VLOG(1) << "AccountReconcilor::OnOAuthError: " << account_id_;
156
157 // Invalidate the access token to force a refetch next time.
158 ProfileOAuth2TokenService* token_service =
159 ProfileOAuth2TokenServiceFactory::GetForProfile(reconcilor_->profile());
160 token_service->InvalidateToken(account_id_, GetScopes(), access_token_);
161
162 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
163 reconcilor_->HandleFailedAccountIdCheck(account_id_);
164 }
165
166 void AccountReconcilor::UserIdFetcher::OnNetworkError(int response_code) {
167 VLOG(1) << "AccountReconcilor::OnNetworkError: " << account_id_
168 << " response_code=" << response_code;
169
170 // TODO(rogerta): some response error should not be treated like
171 // permanent errors. Figure out appropriate ones.
172 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
173 reconcilor_->HandleFailedAccountIdCheck(account_id_);
174 }
175
176 AccountReconcilor::AccountReconcilor(Profile* profile, SigninClient* client)
177 : OAuth2TokenService::Consumer("account_reconcilor"),
178 profile_(profile),
179 client_(client),
180 merge_session_helper_(
181 ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
182 profile->GetRequestContext(),
183 this),
184 registered_with_token_service_(false),
185 is_reconcile_started_(false),
186 are_gaia_accounts_set_(false),
187 requests_(NULL) {
188 VLOG(1) << "AccountReconcilor::AccountReconcilor";
189 }
190
191 AccountReconcilor::~AccountReconcilor() {
192 VLOG(1) << "AccountReconcilor::~AccountReconcilor";
193 // Make sure shutdown was called first.
194 DCHECK(!registered_with_token_service_);
195 DCHECK(!reconciliation_timer_.IsRunning());
196 DCHECK(!requests_);
197 DCHECK_EQ(0u, user_id_fetchers_.size());
198 DCHECK_EQ(0u, refresh_token_fetchers_.size());
199 }
200
201 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) {
202 VLOG(1) << "AccountReconcilor::Initialize";
203 RegisterWithSigninManager();
204
205 // If this profile is not connected, the reconcilor should do nothing but
206 // wait for the connection.
207 if (IsProfileConnected()) {
208 RegisterForCookieChanges();
209 RegisterWithTokenService();
210 StartPeriodicReconciliation();
211
212 // Start a reconcile if the tokens are already loaded.
213 ProfileOAuth2TokenService* token_service =
214 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
215 if (start_reconcile_if_tokens_available &&
216 token_service->GetAccounts().size() > 0) {
217 StartReconcile();
218 }
219 }
220 }
221
222 void AccountReconcilor::Shutdown() {
223 VLOG(1) << "AccountReconcilor::Shutdown";
224 merge_session_helper_.CancelAll();
225 merge_session_helper_.RemoveObserver(this);
226 gaia_fetcher_.reset();
227 DeleteFetchers();
228 UnregisterWithSigninManager();
229 UnregisterWithTokenService();
230 UnregisterForCookieChanges();
231 StopPeriodicReconciliation();
232 }
233
234 void AccountReconcilor::AddMergeSessionObserver(
235 MergeSessionHelper::Observer* observer) {
236 merge_session_helper_.AddObserver(observer);
237 }
238
239 void AccountReconcilor::RemoveMergeSessionObserver(
240 MergeSessionHelper::Observer* observer) {
241 merge_session_helper_.RemoveObserver(observer);
242 }
243
244 void AccountReconcilor::DeleteFetchers() {
245 delete[] requests_;
246 requests_ = NULL;
247
248 user_id_fetchers_.clear();
249 refresh_token_fetchers_.clear();
250 }
251
252 bool AccountReconcilor::AreAllRefreshTokensChecked() const {
253 return chrome_accounts_.size() ==
254 (valid_chrome_accounts_.size() + invalid_chrome_accounts_.size());
255 }
256
257 void AccountReconcilor::RegisterForCookieChanges() {
258 // First clear any existing registration to avoid DCHECKs that can otherwise
259 // go off in some embedders on reauth (e.g., ChromeSigninClient).
260 UnregisterForCookieChanges();
261 client_->SetCookieChangedCallback(
262 base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this)));
263 }
264
265 void AccountReconcilor::UnregisterForCookieChanges() {
266 client_->SetCookieChangedCallback(SigninClient::CookieChangedCallback());
267 }
268
269 void AccountReconcilor::RegisterWithSigninManager() {
270 SigninManagerBase* signin_manager =
271 SigninManagerFactory::GetForProfile(profile_);
272 signin_manager->AddObserver(this);
273 }
274
275 void AccountReconcilor::UnregisterWithSigninManager() {
276 SigninManagerBase* signin_manager =
277 SigninManagerFactory::GetForProfile(profile_);
278 signin_manager->RemoveObserver(this);
279 }
280
281 void AccountReconcilor::RegisterWithTokenService() {
282 VLOG(1) << "AccountReconcilor::RegisterWithTokenService";
283 // During re-auth, the reconcilor will get a callback about successful signin
284 // even when the profile is already connected. Avoid re-registering
285 // with the token service since this will DCHECK.
286 if (registered_with_token_service_)
287 return;
288
289 ProfileOAuth2TokenService* token_service =
290 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
291 token_service->AddObserver(this);
292 registered_with_token_service_ = true;
293 }
294
295 void AccountReconcilor::UnregisterWithTokenService() {
296 if (!registered_with_token_service_)
297 return;
298
299 ProfileOAuth2TokenService* token_service =
300 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
301 token_service->RemoveObserver(this);
302 registered_with_token_service_ = false;
303 }
304
305 bool AccountReconcilor::IsProfileConnected() {
306 return !SigninManagerFactory::GetForProfile(profile_)->
307 GetAuthenticatedUsername().empty();
308 }
309
310 void AccountReconcilor::StartPeriodicReconciliation() {
311 VLOG(1) << "AccountReconcilor::StartPeriodicReconciliation";
312 // TODO(rogerta): pick appropriate thread and timeout value.
313 reconciliation_timer_.Start(
314 FROM_HERE,
315 base::TimeDelta::FromSeconds(300),
316 this,
317 &AccountReconcilor::PeriodicReconciliation);
318 }
319
320 void AccountReconcilor::StopPeriodicReconciliation() {
321 VLOG(1) << "AccountReconcilor::StopPeriodicReconciliation";
322 reconciliation_timer_.Stop();
323 }
324
325 void AccountReconcilor::PeriodicReconciliation() {
326 VLOG(1) << "AccountReconcilor::PeriodicReconciliation";
327 StartReconcile();
328 }
329
330 void AccountReconcilor::OnCookieChanged(const net::CanonicalCookie* cookie) {
331 if (cookie->Name() == "LSID" &&
332 cookie->Domain() == GaiaUrls::GetInstance()->gaia_url().host() &&
333 cookie->IsSecure() && cookie->IsHttpOnly()) {
334 VLOG(1) << "AccountReconcilor::OnCookieChanged: LSID changed";
335 #ifdef OS_CHROMEOS
336 // On Chrome OS it is possible that O2RT is not available at this moment
337 // because profile data transfer is still in progress.
338 ProfileOAuth2TokenService* token_service =
339 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
340 if (!token_service->GetAccounts().size()) {
341 VLOG(1) << "AccountReconcilor::OnCookieChanged: cookie change is ingored"
342 "because profile data transfer is in progress.";
343 return;
344 }
345 #endif
346 StartReconcile();
347 }
348 }
349
350 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) {
351 VLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id;
352 StartReconcile();
353 }
354
355 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) {
356 VLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id;
357 StartRemoveAction(account_id);
358 }
359
360 void AccountReconcilor::OnRefreshTokensLoaded() {}
361
362 void AccountReconcilor::GoogleSigninSucceeded(
363 const std::string& username, const std::string& password) {
364 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in";
365 RegisterForCookieChanges();
366 RegisterWithTokenService();
367 StartPeriodicReconciliation();
368 }
369
370 void AccountReconcilor::GoogleSignedOut(const std::string& username) {
371 VLOG(1) << "AccountReconcilor::GoogleSignedOut: signed out";
372 UnregisterWithTokenService();
373 UnregisterForCookieChanges();
374 StopPeriodicReconciliation();
375 }
376
377 void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
378 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id;
379 merge_session_helper_.LogIn(account_id);
380 }
381
382 void AccountReconcilor::StartRemoveAction(const std::string& account_id) {
383 VLOG(1) << "AccountReconcilor::StartRemoveAction: " << account_id;
384 GetAccountsFromCookie(
385 base::Bind(&AccountReconcilor::FinishRemoveAction,
386 base::Unretained(this),
387 account_id));
388 }
389
390 void AccountReconcilor::FinishRemoveAction(
391 const std::string& account_id,
392 const GoogleServiceAuthError& error,
393 const std::vector<std::pair<std::string, bool> >& accounts) {
394 VLOG(1) << "AccountReconcilor::FinishRemoveAction:"
395 << " account=" << account_id
396 << " error=" << error.ToString();
397 if (error.state() == GoogleServiceAuthError::NONE) {
398 AbortReconcile();
399 std::vector<std::string> accounts_only;
400 for (std::vector<std::pair<std::string, bool> >::const_iterator i =
401 accounts.begin(); i != accounts.end(); ++i) {
402 accounts_only.push_back(i->first);
403 }
404 merge_session_helper_.LogOut(account_id, accounts_only);
405 }
406 // Wait for the next ReconcileAction if there is an error.
407 }
408
409 void AccountReconcilor::PerformAddToChromeAction(
410 const std::string& account_id,
411 int session_index) {
412 VLOG(1) << "AccountReconcilor::PerformAddToChromeAction:"
413 << " account=" << account_id
414 << " session_index=" << session_index;
415
416 #if !defined(OS_ANDROID) && !defined(OS_IOS)
417 refresh_token_fetchers_.push_back(
418 new RefreshTokenFetcher(this, account_id, session_index));
419 #endif
420 }
421
422 void AccountReconcilor::PerformLogoutAllAccountsAction() {
423 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction";
424 merge_session_helper_.LogOutAllAccounts();
425 }
426
427 void AccountReconcilor::StartReconcile() {
428 if (!IsProfileConnected() || is_reconcile_started_)
429 return;
430
431 is_reconcile_started_ = true;
432
433 // Reset state for validating gaia cookie.
434 are_gaia_accounts_set_ = false;
435 gaia_accounts_.clear();
436 GetAccountsFromCookie(base::Bind(
437 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
438 base::Unretained(this)));
439
440 // Reset state for validating oauth2 tokens.
441 primary_account_.clear();
442 chrome_accounts_.clear();
443 DeleteFetchers();
444 valid_chrome_accounts_.clear();
445 invalid_chrome_accounts_.clear();
446 add_to_cookie_.clear();
447 add_to_chrome_.clear();
448 ValidateAccountsFromTokenService();
449 }
450
451 void AccountReconcilor::GetAccountsFromCookie(
452 GetAccountsFromCookieCallback callback) {
453 get_gaia_accounts_callbacks_.push_back(callback);
454 if (!gaia_fetcher_) {
455 // There is no list account request in flight.
456 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
457 profile_->GetRequestContext()));
458 gaia_fetcher_->StartListAccounts();
459 }
460 }
461
462 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) {
463 gaia_fetcher_.reset();
464
465 // Get account information from response data.
466 std::vector<std::pair<std::string, bool> > gaia_accounts;
467 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
468 if (!valid_json) {
469 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: parsing error";
470 } else if (gaia_accounts.size() > 0) {
471 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: "
472 << "Gaia " << gaia_accounts.size() << " accounts, "
473 << "Primary is '" << gaia_accounts[0].first << "'";
474 } else {
475 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts";
476 }
477
478 // There must be at least one callback waiting for result.
479 DCHECK(!get_gaia_accounts_callbacks_.empty());
480
481 GoogleServiceAuthError error = !valid_json
482 ? GoogleServiceAuthError(
483 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE)
484 : GoogleServiceAuthError::AuthErrorNone();
485 get_gaia_accounts_callbacks_.front().Run(error, gaia_accounts);
486 get_gaia_accounts_callbacks_.pop_front();
487
488 MayBeDoNextListAccounts();
489 }
490
491 void AccountReconcilor::OnListAccountsFailure(
492 const GoogleServiceAuthError& error) {
493 gaia_fetcher_.reset();
494 VLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString();
495 std::vector<std::pair<std::string, bool> > empty_accounts;
496
497 // There must be at least one callback waiting for result.
498 DCHECK(!get_gaia_accounts_callbacks_.empty());
499
500 get_gaia_accounts_callbacks_.front().Run(error, empty_accounts);
501 get_gaia_accounts_callbacks_.pop_front();
502
503 MayBeDoNextListAccounts();
504 }
505
506 void AccountReconcilor::MayBeDoNextListAccounts() {
507 if (!get_gaia_accounts_callbacks_.empty()) {
508 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
509 profile_->GetRequestContext()));
510 gaia_fetcher_->StartListAccounts();
511 }
512 }
513
514 void AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts(
515 const GoogleServiceAuthError& error,
516 const std::vector<std::pair<std::string, bool> >& accounts) {
517 if (error.state() == GoogleServiceAuthError::NONE) {
518 gaia_accounts_ = accounts;
519 are_gaia_accounts_set_ = true;
520 FinishReconcile();
521 } else {
522 AbortReconcile();
523 }
524 }
525
526 void AccountReconcilor::ValidateAccountsFromTokenService() {
527 primary_account_ =
528 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
529 DCHECK(!primary_account_.empty());
530
531 ProfileOAuth2TokenService* token_service =
532 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
533 chrome_accounts_ = token_service->GetAccounts();
534 DCHECK_GT(chrome_accounts_.size(), 0u);
535
536 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: "
537 << "Chrome " << chrome_accounts_.size() << " accounts, "
538 << "Primary is '" << primary_account_ << "'";
539
540 DCHECK(!requests_);
541 requests_ =
542 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()];
543 const OAuth2TokenService::ScopeSet scopes =
544 AccountReconcilor::UserIdFetcher::GetScopes();
545 for (size_t i = 0; i < chrome_accounts_.size(); ++i) {
546 requests_[i] = token_service->StartRequest(chrome_accounts_[i],
547 scopes,
548 this);
549 }
550
551 DCHECK_EQ(0u, user_id_fetchers_.size());
552 user_id_fetchers_.resize(chrome_accounts_.size());
553 }
554
555 void AccountReconcilor::OnGetTokenSuccess(
556 const OAuth2TokenService::Request* request,
557 const std::string& access_token,
558 const base::Time& expiration_time) {
559 size_t index;
560 for (index = 0; index < chrome_accounts_.size(); ++index) {
561 if (request == requests_[index].get())
562 break;
563 }
564 DCHECK(index < chrome_accounts_.size());
565
566 const std::string& account_id = chrome_accounts_[index];
567
568 VLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " << account_id;
569
570 DCHECK(!user_id_fetchers_[index]);
571 user_id_fetchers_[index] =
572 new UserIdFetcher(this, access_token, account_id);
573 }
574
575 void AccountReconcilor::OnGetTokenFailure(
576 const OAuth2TokenService::Request* request,
577 const GoogleServiceAuthError& error) {
578 size_t index;
579 for (index = 0; index < chrome_accounts_.size(); ++index) {
580 if (request == requests_[index].get())
581 break;
582 }
583 DCHECK(index < chrome_accounts_.size());
584
585 const std::string& account_id = chrome_accounts_[index];
586
587 VLOG(1) << "AccountReconcilor::OnGetTokenFailure: invalid "
588 << account_id;
589 HandleFailedAccountIdCheck(account_id);
590 }
591
592 void AccountReconcilor::FinishReconcile() {
593 // Make sure that the process of validating the gaia cookie and the oauth2
594 // tokens individually is done before proceeding with reconciliation.
595 if (!are_gaia_accounts_set_ || !AreAllRefreshTokensChecked())
596 return;
597
598 VLOG(1) << "AccountReconcilor::FinishReconcile";
599
600 DeleteFetchers();
601
602 DCHECK(add_to_cookie_.empty());
603 DCHECK(add_to_chrome_.empty());
604 bool are_primaries_equal =
605 gaia_accounts_.size() > 0 &&
606 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first);
607
608 if (are_primaries_equal) {
609 // Determine if we need to merge accounts from gaia cookie to chrome.
610 for (size_t i = 0; i < gaia_accounts_.size(); ++i) {
611 const std::string& gaia_account = gaia_accounts_[i].first;
612 if (gaia_accounts_[i].second &&
613 valid_chrome_accounts_.find(gaia_account) ==
614 valid_chrome_accounts_.end()) {
615 add_to_chrome_.push_back(std::make_pair(gaia_account, i));
616 }
617 }
618
619 // Determine if we need to merge accounts from chrome into gaia cookie.
620 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
621 i != valid_chrome_accounts_.end(); ++i) {
622 bool add_to_cookie = true;
623 for (size_t j = 0; j < gaia_accounts_.size(); ++j) {
624 if (gaia::AreEmailsSame(gaia_accounts_[j].first, *i)) {
625 add_to_cookie = !gaia_accounts_[j].second;
626 break;
627 }
628 }
629 if (add_to_cookie)
630 add_to_cookie_.push_back(*i);
631 }
632 } else {
633 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie";
634 // Really messed up state. Blow away the gaia cookie completely and
635 // rebuild it, making sure the primary account as specified by the
636 // SigninManager is the first session in the gaia cookie.
637 PerformLogoutAllAccountsAction();
638 add_to_cookie_.push_back(primary_account_);
639 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
640 i != valid_chrome_accounts_.end(); ++i) {
641 if (*i != primary_account_)
642 add_to_cookie_.push_back(*i);
643 }
644 }
645
646 // For each account known to chrome but not in the gaia cookie,
647 // PerformMergeAction().
648 for (size_t i = 0; i < add_to_cookie_.size(); ++i)
649 PerformMergeAction(add_to_cookie_[i]);
650
651 // For each account in the gaia cookie not known to chrome,
652 // PerformAddToChromeAction.
653 for (std::vector<std::pair<std::string, int> >::const_iterator i =
654 add_to_chrome_.begin();
655 i != add_to_chrome_.end(); ++i) {
656 PerformAddToChromeAction(i->first, i->second);
657 }
658
659 CalculateIfReconcileIsDone();
660 ScheduleStartReconcileIfChromeAccountsChanged();
661 }
662
663 void AccountReconcilor::AbortReconcile() {
664 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later";
665 DeleteFetchers();
666 add_to_cookie_.clear();
667 add_to_chrome_.clear();
668 CalculateIfReconcileIsDone();
669 }
670
671 void AccountReconcilor::CalculateIfReconcileIsDone() {
672 is_reconcile_started_ = !add_to_cookie_.empty() || !add_to_chrome_.empty();
673 if (!is_reconcile_started_)
674 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done";
675 }
676
677 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() {
678 if (is_reconcile_started_)
679 return;
680
681 // Start a reconcile as the token accounts have changed.
682 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged";
683 std::vector<std::string> reconciled_accounts(chrome_accounts_);
684 std::vector<std::string> new_chrome_accounts(
685 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts());
686 std::sort(reconciled_accounts.begin(), reconciled_accounts.end());
687 std::sort(new_chrome_accounts.begin(), new_chrome_accounts.end());
688 if (reconciled_accounts != new_chrome_accounts) {
689 base::MessageLoop::current()->PostTask(
690 FROM_HERE,
691 base::Bind(&AccountReconcilor::StartReconcile, base::Unretained(this)));
692 }
693 }
694
695 void AccountReconcilor::MergeSessionCompleted(
696 const std::string& account_id,
697 const GoogleServiceAuthError& error) {
698 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id="
699 << account_id;
700
701 // Remove the account from the list that is being merged.
702 for (std::vector<std::string>::iterator i = add_to_cookie_.begin();
703 i != add_to_cookie_.end(); ++i) {
704 if (account_id == *i) {
705 add_to_cookie_.erase(i);
706 break;
707 }
708 }
709
710 CalculateIfReconcileIsDone();
711 ScheduleStartReconcileIfChromeAccountsChanged();
712 }
713
714 void AccountReconcilor::HandleSuccessfulAccountIdCheck(
715 const std::string& account_id) {
716 valid_chrome_accounts_.insert(account_id);
717 FinishReconcile();
718 }
719
720 void AccountReconcilor::HandleFailedAccountIdCheck(
721 const std::string& account_id) {
722 invalid_chrome_accounts_.insert(account_id);
723 FinishReconcile();
724 }
725
726 void AccountReconcilor::HandleRefreshTokenFetched(
727 const std::string& account_id,
728 const std::string& refresh_token) {
729 if (!refresh_token.empty()) {
730 ProfileOAuth2TokenService* token_service =
731 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
732 token_service->UpdateCredentials(account_id, refresh_token);
733 }
734
735 // Remove the account from the list that is being updated.
736 for (std::vector<std::pair<std::string, int> >::iterator i =
737 add_to_chrome_.begin();
738 i != add_to_chrome_.end(); ++i) {
739 if (gaia::AreEmailsSame(account_id, i->first)) {
740 add_to_chrome_.erase(i);
741 break;
742 }
743 }
744
745 CalculateIfReconcileIsDone();
746 }
OLDNEW
« no previous file with comments | « chrome/browser/signin/account_reconcilor.h ('k') | chrome/browser/signin/account_reconcilor_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698