| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/logging.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/net/chrome_cookie_notification_details.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/account_reconcilor.h" |
| #include "chrome/browser/signin/google_auto_login_helper.h" |
| #include "chrome/browser/signin/profile_oauth2_token_service.h" |
| #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
| #include "chrome/browser/signin/signin_manager.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_source.h" |
| #include "google_apis/gaia/gaia_auth_fetcher.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "google_apis/gaia/gaia_constants.h" |
| |
| AccountReconcilor::AccountReconcilor(Profile* profile) |
| : profile_(profile), |
| are_gaia_accounts_set_(false), |
| requests_(NULL) { |
| DVLOG(1) << "AccountReconcilor::AccountReconcilor"; |
| RegisterWithSigninManager(); |
| RegisterWithCookieMonster(); |
| |
| // If this profile is not connected, the reconcilor should do nothing but |
| // wait for the connection. |
| if (IsProfileConnected()) { |
| RegisterWithTokenService(); |
| StartPeriodicReconciliation(); |
| } |
| } |
| |
| AccountReconcilor::~AccountReconcilor() { |
| // Make sure shutdown was called first. |
| DCHECK(registrar_.IsEmpty()); |
| DCHECK(!reconciliation_timer_.IsRunning()); |
| DCHECK(!requests_); |
| } |
| |
| void AccountReconcilor::Shutdown() { |
| DVLOG(1) << "AccountReconcilor::Shutdown"; |
| DeleteAccessTokenRequests(); |
| UnregisterWithSigninManager(); |
| UnregisterWithTokenService(); |
| UnregisterWithCookieMonster(); |
| StopPeriodicReconciliation(); |
| } |
| |
| void AccountReconcilor::DeleteAccessTokenRequests() { |
| delete[] requests_; |
| requests_ = NULL; |
| } |
| |
| void AccountReconcilor::RegisterWithCookieMonster() { |
| content::Source<Profile> source(profile_); |
| registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); |
| } |
| |
| void AccountReconcilor::UnregisterWithCookieMonster() { |
| content::Source<Profile> source(profile_); |
| registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); |
| } |
| |
| void AccountReconcilor::RegisterWithSigninManager() { |
| content::Source<Profile> source(profile_); |
| registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, source); |
| registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, source); |
| } |
| |
| void AccountReconcilor::UnregisterWithSigninManager() { |
| content::Source<Profile> source(profile_); |
| registrar_.Remove( |
| this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, source); |
| registrar_.Remove(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, source); |
| } |
| |
| void AccountReconcilor::RegisterWithTokenService() { |
| DVLOG(1) << "AccountReconcilor::RegisterWithTokenService"; |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| token_service->AddObserver(this); |
| } |
| |
| void AccountReconcilor::UnregisterWithTokenService() { |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| token_service->RemoveObserver(this); |
| } |
| |
| bool AccountReconcilor::IsProfileConnected() { |
| return !SigninManagerFactory::GetForProfile(profile_)-> |
| GetAuthenticatedUsername().empty(); |
| } |
| |
| void AccountReconcilor::StartPeriodicReconciliation() { |
| DVLOG(1) << "AccountReconcilor::StartPeriodicReconciliation"; |
| // TODO(rogerta): pick appropriate thread and timeout value. |
| reconciliation_timer_.Start( |
| FROM_HERE, |
| base::TimeDelta::FromSeconds(300), |
| this, |
| &AccountReconcilor::PeriodicReconciliation); |
| } |
| |
| void AccountReconcilor::StopPeriodicReconciliation() { |
| DVLOG(1) << "AccountReconcilor::StopPeriodicReconciliation"; |
| reconciliation_timer_.Stop(); |
| } |
| |
| void AccountReconcilor::PeriodicReconciliation() { |
| DVLOG(1) << "AccountReconcilor::PeriodicReconciliation"; |
| StartReconcileAction(); |
| } |
| |
| void AccountReconcilor::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| switch (type) { |
| case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: |
| DVLOG(1) << "AccountReconcilor::Observe: signed in"; |
| RegisterWithTokenService(); |
| StartPeriodicReconciliation(); |
| break; |
| case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: |
| DVLOG(1) << "AccountReconcilor::Observe: signed out"; |
| UnregisterWithTokenService(); |
| StopPeriodicReconciliation(); |
| break; |
| case chrome::NOTIFICATION_COOKIE_CHANGED: |
| OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) { |
| // TODO(acleung): Filter out cookies by looking at the domain. |
| // StartReconcileAction(); |
| } |
| |
| void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) { |
| DVLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id; |
| PerformMergeAction(account_id); |
| } |
| |
| void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { |
| DVLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id; |
| PerformRemoveAction(account_id); |
| } |
| |
| void AccountReconcilor::OnRefreshTokensLoaded() {} |
| |
| void AccountReconcilor::PerformMergeAction(const std::string& account_id) { |
| // GoogleAutoLoginHelper deletes itself upon success / failure. |
| GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_); |
| helper->LogIn(account_id); |
| } |
| |
| void AccountReconcilor::PerformRemoveAction(const std::string& account_id) { |
| // TODO(acleung): Implement this: |
| } |
| |
| void AccountReconcilor::StartReconcileAction() { |
| if (!IsProfileConnected()) |
| return; |
| |
| // Reset state for validating gaia cookie. |
| are_gaia_accounts_set_ = false; |
| gaia_accounts_.clear(); |
| GetAccountsFromCookie(); |
| |
| // Reset state for validating oauth2 tokens. |
| primary_account_.clear(); |
| chrome_accounts_.clear(); |
| DeleteAccessTokenRequests(); |
| valid_chrome_accounts_.clear(); |
| invalid_chrome_accounts_.clear(); |
| ValidateAccountsFromTokenService(); |
| } |
| |
| void AccountReconcilor::GetAccountsFromCookie() { |
| gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, |
| profile_->GetRequestContext())); |
| gaia_fetcher_->StartListAccounts(); |
| } |
| |
| void AccountReconcilor::OnListAccountsSuccess(const std::string& data) { |
| gaia_fetcher_.reset(); |
| |
| // Get account information from response data. |
| gaia_accounts_ = gaia::ParseListAccountsData(data); |
| if (gaia_accounts_.size() > 0) { |
| DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: " |
| << "Gaia " << gaia_accounts_.size() << " accounts, " |
| << "Primary is '" << gaia_accounts_[0] << "'"; |
| } else { |
| DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts"; |
| } |
| |
| are_gaia_accounts_set_ = true; |
| FinishReconcileAction(); |
| } |
| |
| void AccountReconcilor::OnListAccountsFailure( |
| const GoogleServiceAuthError& error) { |
| gaia_fetcher_.reset(); |
| DVLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString(); |
| |
| are_gaia_accounts_set_ = true; |
| FinishReconcileAction(); |
| } |
| |
| void AccountReconcilor::ValidateAccountsFromTokenService() { |
| primary_account_ = |
| SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername(); |
| DCHECK(!primary_account_.empty()); |
| |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| chrome_accounts_ = token_service->GetAccounts(); |
| DCHECK(chrome_accounts_.size() > 0); |
| |
| DVLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " |
| << "Chrome " << chrome_accounts_.size() << " accounts, " |
| << "Primary is '" << primary_account_ << "'"; |
| |
| DCHECK(!requests_); |
| requests_ = |
| new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()]; |
| for (size_t i = 0; i < chrome_accounts_.size(); ++i) { |
| requests_[i] = token_service->StartRequest(chrome_accounts_[i], |
| OAuth2TokenService::ScopeSet(), |
| this); |
| } |
| } |
| |
| void AccountReconcilor::OnGetTokenSuccess( |
| const OAuth2TokenService::Request* request, |
| const std::string& access_token, |
| const base::Time& expiration_time) { |
| DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " |
| << request->GetAccountId(); |
| valid_chrome_accounts_.insert(request->GetAccountId()); |
| FinishReconcileAction(); |
| } |
| |
| void AccountReconcilor::OnGetTokenFailure( |
| const OAuth2TokenService::Request* request, |
| const GoogleServiceAuthError& error) { |
| DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: invalid " |
| << request->GetAccountId(); |
| invalid_chrome_accounts_.insert(request->GetAccountId()); |
| FinishReconcileAction(); |
| } |
| |
| void AccountReconcilor::FinishReconcileAction() { |
| // Make sure that the process of validating the gaia cookie and the oauth2 |
| // tokens individually is done before proceeding with reconciliation. |
| if (!are_gaia_accounts_set_ || |
| (chrome_accounts_.size() != (valid_chrome_accounts_.size() + |
| invalid_chrome_accounts_.size()))) { |
| return; |
| } |
| |
| DVLOG(1) << "AccountReconcilor::FinishReconcileAction"; |
| |
| bool are_primaries_equal = |
| gaia_accounts_.size() > 0 && primary_account_ == gaia_accounts_[0]; |
| bool have_same_accounts = chrome_accounts_.size() == gaia_accounts_.size(); |
| if (have_same_accounts) { |
| for (size_t i = 0; i < gaia_accounts_.size(); ++i) { |
| if (std::find(chrome_accounts_.begin(), chrome_accounts_.end(), |
| gaia_accounts_[i]) == chrome_accounts_.end()) { |
| have_same_accounts = false; |
| break; |
| } |
| } |
| } |
| |
| if (!are_primaries_equal || !have_same_accounts) { |
| // TODO(rogerta): fix things up. |
| } |
| } |