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