• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/logging.h"
6 #include "base/time/time.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/net/chrome_cookie_notification_details.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/signin/account_reconcilor.h"
11 #include "chrome/browser/signin/google_auto_login_helper.h"
12 #include "chrome/browser/signin/profile_oauth2_token_service.h"
13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14 #include "chrome/browser/signin/signin_manager.h"
15 #include "chrome/browser/signin/signin_manager_factory.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_source.h"
19 #include "google_apis/gaia/gaia_auth_fetcher.h"
20 #include "google_apis/gaia/gaia_auth_util.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 
AccountReconcilor(Profile * profile)23 AccountReconcilor::AccountReconcilor(Profile* profile)
24     : profile_(profile),
25       are_gaia_accounts_set_(false),
26       requests_(NULL) {
27   DVLOG(1) << "AccountReconcilor::AccountReconcilor";
28   RegisterWithSigninManager();
29   RegisterWithCookieMonster();
30 
31   // If this profile is not connected, the reconcilor should do nothing but
32   // wait for the connection.
33   if (IsProfileConnected()) {
34     RegisterWithTokenService();
35     StartPeriodicReconciliation();
36   }
37 }
38 
~AccountReconcilor()39 AccountReconcilor::~AccountReconcilor() {
40   // Make sure shutdown was called first.
41   DCHECK(registrar_.IsEmpty());
42   DCHECK(!reconciliation_timer_.IsRunning());
43   DCHECK(!requests_);
44 }
45 
Shutdown()46 void AccountReconcilor::Shutdown() {
47   DVLOG(1) << "AccountReconcilor::Shutdown";
48   DeleteAccessTokenRequests();
49   UnregisterWithSigninManager();
50   UnregisterWithTokenService();
51   UnregisterWithCookieMonster();
52   StopPeriodicReconciliation();
53 }
54 
DeleteAccessTokenRequests()55 void AccountReconcilor::DeleteAccessTokenRequests() {
56   delete[] requests_;
57   requests_ = NULL;
58 }
59 
RegisterWithCookieMonster()60 void AccountReconcilor::RegisterWithCookieMonster() {
61   content::Source<Profile> source(profile_);
62   registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
63 }
64 
UnregisterWithCookieMonster()65 void AccountReconcilor::UnregisterWithCookieMonster() {
66   content::Source<Profile> source(profile_);
67   registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
68 }
69 
RegisterWithSigninManager()70 void AccountReconcilor::RegisterWithSigninManager() {
71   content::Source<Profile> source(profile_);
72   registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, source);
73   registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, source);
74 }
75 
UnregisterWithSigninManager()76 void AccountReconcilor::UnregisterWithSigninManager() {
77   content::Source<Profile> source(profile_);
78   registrar_.Remove(
79       this, chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, source);
80   registrar_.Remove(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, source);
81 }
82 
RegisterWithTokenService()83 void AccountReconcilor::RegisterWithTokenService() {
84   DVLOG(1) << "AccountReconcilor::RegisterWithTokenService";
85   ProfileOAuth2TokenService* token_service =
86       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
87   token_service->AddObserver(this);
88 }
89 
UnregisterWithTokenService()90 void AccountReconcilor::UnregisterWithTokenService() {
91   ProfileOAuth2TokenService* token_service =
92       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
93   token_service->RemoveObserver(this);
94 }
95 
IsProfileConnected()96 bool AccountReconcilor::IsProfileConnected() {
97   return !SigninManagerFactory::GetForProfile(profile_)->
98       GetAuthenticatedUsername().empty();
99 }
100 
StartPeriodicReconciliation()101 void AccountReconcilor::StartPeriodicReconciliation() {
102   DVLOG(1) << "AccountReconcilor::StartPeriodicReconciliation";
103   // TODO(rogerta): pick appropriate thread and timeout value.
104   reconciliation_timer_.Start(
105       FROM_HERE,
106       base::TimeDelta::FromSeconds(300),
107       this,
108       &AccountReconcilor::PeriodicReconciliation);
109 }
110 
StopPeriodicReconciliation()111 void AccountReconcilor::StopPeriodicReconciliation() {
112   DVLOG(1) << "AccountReconcilor::StopPeriodicReconciliation";
113   reconciliation_timer_.Stop();
114 }
115 
PeriodicReconciliation()116 void AccountReconcilor::PeriodicReconciliation() {
117   DVLOG(1) << "AccountReconcilor::PeriodicReconciliation";
118   StartReconcileAction();
119 }
120 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)121 void AccountReconcilor::Observe(int type,
122                                 const content::NotificationSource& source,
123                                 const content::NotificationDetails& details) {
124   switch (type) {
125     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
126       DVLOG(1) << "AccountReconcilor::Observe: signed in";
127       RegisterWithTokenService();
128       StartPeriodicReconciliation();
129       break;
130     case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
131       DVLOG(1) << "AccountReconcilor::Observe: signed out";
132       UnregisterWithTokenService();
133       StopPeriodicReconciliation();
134       break;
135     case chrome::NOTIFICATION_COOKIE_CHANGED:
136       OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr());
137       break;
138     default:
139       NOTREACHED();
140       break;
141   }
142 }
143 
OnCookieChanged(ChromeCookieDetails * details)144 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) {
145   // TODO(acleung): Filter out cookies by looking at the domain.
146   // StartReconcileAction();
147 }
148 
OnRefreshTokenAvailable(const std::string & account_id)149 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) {
150   DVLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id;
151   PerformMergeAction(account_id);
152 }
153 
OnRefreshTokenRevoked(const std::string & account_id)154 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) {
155   DVLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id;
156   PerformRemoveAction(account_id);
157 }
158 
OnRefreshTokensLoaded()159 void AccountReconcilor::OnRefreshTokensLoaded() {}
160 
PerformMergeAction(const std::string & account_id)161 void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
162   // GoogleAutoLoginHelper deletes itself upon success / failure.
163   GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_);
164   helper->LogIn(account_id);
165 }
166 
PerformRemoveAction(const std::string & account_id)167 void AccountReconcilor::PerformRemoveAction(const std::string& account_id) {
168   // TODO(acleung): Implement this:
169 }
170 
StartReconcileAction()171 void AccountReconcilor::StartReconcileAction() {
172   if (!IsProfileConnected())
173     return;
174 
175   // Reset state for validating gaia cookie.
176   are_gaia_accounts_set_ = false;
177   gaia_accounts_.clear();
178   GetAccountsFromCookie();
179 
180   // Reset state for validating oauth2 tokens.
181   primary_account_.clear();
182   chrome_accounts_.clear();
183   DeleteAccessTokenRequests();
184   valid_chrome_accounts_.clear();
185   invalid_chrome_accounts_.clear();
186   ValidateAccountsFromTokenService();
187 }
188 
GetAccountsFromCookie()189 void AccountReconcilor::GetAccountsFromCookie() {
190   gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
191                                           profile_->GetRequestContext()));
192   gaia_fetcher_->StartListAccounts();
193 }
194 
OnListAccountsSuccess(const std::string & data)195 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) {
196   gaia_fetcher_.reset();
197 
198   // Get account information from response data.
199   gaia_accounts_ = gaia::ParseListAccountsData(data);
200   if (gaia_accounts_.size() > 0) {
201     DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: "
202              << "Gaia " << gaia_accounts_.size() << " accounts, "
203              << "Primary is '" << gaia_accounts_[0] << "'";
204   } else {
205     DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts";
206   }
207 
208   are_gaia_accounts_set_ = true;
209   FinishReconcileAction();
210 }
211 
OnListAccountsFailure(const GoogleServiceAuthError & error)212 void AccountReconcilor::OnListAccountsFailure(
213     const GoogleServiceAuthError& error) {
214   gaia_fetcher_.reset();
215   DVLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString();
216 
217   are_gaia_accounts_set_ = true;
218   FinishReconcileAction();
219 }
220 
ValidateAccountsFromTokenService()221 void AccountReconcilor::ValidateAccountsFromTokenService() {
222   primary_account_ =
223       SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
224   DCHECK(!primary_account_.empty());
225 
226   ProfileOAuth2TokenService* token_service =
227       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
228   chrome_accounts_ = token_service->GetAccounts();
229   DCHECK(chrome_accounts_.size() > 0);
230 
231   DVLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: "
232             << "Chrome " << chrome_accounts_.size() << " accounts, "
233             << "Primary is '" << primary_account_ << "'";
234 
235   DCHECK(!requests_);
236   requests_ =
237       new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()];
238   for (size_t i = 0; i < chrome_accounts_.size(); ++i) {
239     requests_[i] = token_service->StartRequest(chrome_accounts_[i],
240                                                OAuth2TokenService::ScopeSet(),
241                                                this);
242   }
243 }
244 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)245 void AccountReconcilor::OnGetTokenSuccess(
246     const OAuth2TokenService::Request* request,
247     const std::string& access_token,
248     const base::Time& expiration_time) {
249   DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid "
250            << request->GetAccountId();
251   valid_chrome_accounts_.insert(request->GetAccountId());
252   FinishReconcileAction();
253 }
254 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)255 void AccountReconcilor::OnGetTokenFailure(
256     const OAuth2TokenService::Request* request,
257     const GoogleServiceAuthError& error) {
258   DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: invalid "
259            << request->GetAccountId();
260   invalid_chrome_accounts_.insert(request->GetAccountId());
261   FinishReconcileAction();
262 }
263 
FinishReconcileAction()264 void AccountReconcilor::FinishReconcileAction() {
265   // Make sure that the process of validating the gaia cookie and the oauth2
266   // tokens individually is done before proceeding with reconciliation.
267   if (!are_gaia_accounts_set_ ||
268       (chrome_accounts_.size() != (valid_chrome_accounts_.size() +
269                                    invalid_chrome_accounts_.size()))) {
270     return;
271   }
272 
273   DVLOG(1) << "AccountReconcilor::FinishReconcileAction";
274 
275   bool are_primaries_equal =
276       gaia_accounts_.size() > 0 && primary_account_ == gaia_accounts_[0];
277   bool have_same_accounts = chrome_accounts_.size() == gaia_accounts_.size();
278   if (have_same_accounts) {
279     for (size_t i = 0; i < gaia_accounts_.size(); ++i) {
280       if (std::find(chrome_accounts_.begin(), chrome_accounts_.end(),
281               gaia_accounts_[i]) == chrome_accounts_.end()) {
282         have_same_accounts = false;
283         break;
284       }
285     }
286   }
287 
288   if (!are_primaries_equal || !have_same_accounts) {
289     // TODO(rogerta): fix things up.
290   }
291 }
292