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