| OLD | NEW |
| (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 } | |
| OLD | NEW |