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