OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/signin/core/browser/account_reconcilor.h" | 5 #include "components/signin/core/browser/account_reconcilor.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
13 #include "base/message_loop/message_loop_proxy.h" | 13 #include "base/message_loop/message_loop_proxy.h" |
14 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
16 #include "components/signin/core/browser/profile_oauth2_token_service.h" | 16 #include "components/signin/core/browser/profile_oauth2_token_service.h" |
17 #include "components/signin/core/browser/signin_client.h" | 17 #include "components/signin/core/browser/signin_client.h" |
18 #include "components/signin/core/browser/signin_metrics.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" | 19 #include "components/signin/core/common/profile_management_switches.h" |
21 #include "google_apis/gaia/gaia_auth_fetcher.h" | 20 #include "google_apis/gaia/gaia_auth_fetcher.h" |
22 #include "google_apis/gaia/gaia_auth_util.h" | 21 #include "google_apis/gaia/gaia_auth_util.h" |
23 #include "google_apis/gaia/gaia_constants.h" | 22 #include "google_apis/gaia/gaia_constants.h" |
24 #include "google_apis/gaia/gaia_oauth_client.h" | 23 #include "google_apis/gaia/gaia_oauth_client.h" |
25 #include "google_apis/gaia/gaia_urls.h" | 24 #include "google_apis/gaia/gaia_urls.h" |
26 #include "net/cookies/canonical_cookie.h" | 25 #include "net/cookies/canonical_cookie.h" |
27 | 26 |
28 | 27 |
29 namespace { | 28 namespace { |
30 | 29 |
31 class EmailEqualToFunc : public std::equal_to<std::pair<std::string, bool> > { | 30 class EmailEqualToFunc : public std::equal_to<std::pair<std::string, bool> > { |
32 public: | 31 public: |
33 bool operator()(const std::pair<std::string, bool>& p1, | 32 bool operator()(const std::pair<std::string, bool>& p1, |
34 const std::pair<std::string, bool>& p2) const; | 33 const std::pair<std::string, bool>& p2) const; |
35 }; | 34 }; |
36 | 35 |
37 bool EmailEqualToFunc::operator()( | 36 bool EmailEqualToFunc::operator()( |
38 const std::pair<std::string, bool>& p1, | 37 const std::pair<std::string, bool>& p1, |
39 const std::pair<std::string, bool>& p2) const { | 38 const std::pair<std::string, bool>& p2) const { |
40 return p1.second == p2.second && gaia::AreEmailsSame(p1.first, p2.first); | 39 return p1.second == p2.second && gaia::AreEmailsSame(p1.first, p2.first); |
41 } | 40 } |
42 | 41 |
| 42 class AreEmailsSameFunc : public std::equal_to<std::string> { |
| 43 public: |
| 44 bool operator()(const std::string& p1, |
| 45 const std::string& p2) const; |
| 46 }; |
| 47 |
| 48 bool AreEmailsSameFunc::operator()( |
| 49 const std::string& p1, |
| 50 const std::string& p2) const { |
| 51 return gaia::AreEmailsSame(p1, p2); |
| 52 } |
| 53 |
43 } // namespace | 54 } // namespace |
44 | 55 |
45 | 56 |
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. | |
49 class 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 | |
77 AccountReconcilor::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 | |
93 void 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 | |
108 void 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 | |
115 bool AccountReconcilor::EmailLessFunc::operator()(const std::string& s1, | |
116 const std::string& s2) const { | |
117 return gaia::CanonicalizeEmail(s1) < gaia::CanonicalizeEmail(s2); | |
118 } | |
119 | |
120 class 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 | |
144 AccountReconcilor::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 | |
159 OAuth2TokenService::ScopeSet AccountReconcilor::UserIdFetcher::GetScopes() { | |
160 OAuth2TokenService::ScopeSet scopes; | |
161 scopes.insert("https://www.googleapis.com/auth/userinfo.profile"); | |
162 return scopes; | |
163 } | |
164 | |
165 void 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 | |
173 void 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 | |
184 void 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 | |
194 AccountReconcilor::AccountReconcilor(ProfileOAuth2TokenService* token_service, | 57 AccountReconcilor::AccountReconcilor(ProfileOAuth2TokenService* token_service, |
195 SigninManagerBase* signin_manager, | 58 SigninManagerBase* signin_manager, |
196 SigninClient* client) | 59 SigninClient* client) |
197 : OAuth2TokenService::Consumer("account_reconcilor"), | 60 : token_service_(token_service), |
198 token_service_(token_service), | |
199 signin_manager_(signin_manager), | 61 signin_manager_(signin_manager), |
200 client_(client), | 62 client_(client), |
201 merge_session_helper_(token_service_, | 63 merge_session_helper_(token_service_, |
202 client->GetURLRequestContext(), | 64 client->GetURLRequestContext(), |
203 this), | 65 this), |
204 registered_with_token_service_(false), | 66 registered_with_token_service_(false), |
205 is_reconcile_started_(false), | 67 is_reconcile_started_(false), |
206 first_execution_(true), | 68 first_execution_(true), |
207 are_gaia_accounts_set_(false), | 69 are_gaia_accounts_set_(false) { |
208 requests_(NULL) { | |
209 VLOG(1) << "AccountReconcilor::AccountReconcilor"; | 70 VLOG(1) << "AccountReconcilor::AccountReconcilor"; |
210 } | 71 } |
211 | 72 |
212 AccountReconcilor::~AccountReconcilor() { | 73 AccountReconcilor::~AccountReconcilor() { |
213 VLOG(1) << "AccountReconcilor::~AccountReconcilor"; | 74 VLOG(1) << "AccountReconcilor::~AccountReconcilor"; |
214 // Make sure shutdown was called first. | 75 // Make sure shutdown was called first. |
215 DCHECK(!registered_with_token_service_); | 76 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 } | 77 } |
220 | 78 |
221 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) { | 79 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) { |
222 VLOG(1) << "AccountReconcilor::Initialize"; | 80 VLOG(1) << "AccountReconcilor::Initialize"; |
223 RegisterWithSigninManager(); | 81 RegisterWithSigninManager(); |
224 | 82 |
225 // If this user is not signed in, the reconcilor should do nothing but | 83 // If this user is not signed in, the reconcilor should do nothing but |
226 // wait for signin. | 84 // wait for signin. |
227 if (IsProfileConnected()) { | 85 if (IsProfileConnected()) { |
228 RegisterForCookieChanges(); | 86 RegisterForCookieChanges(); |
229 RegisterWithTokenService(); | 87 RegisterWithTokenService(); |
230 | 88 |
231 // Start a reconcile if the tokens are already loaded. | 89 // Start a reconcile if the tokens are already loaded. |
232 if (start_reconcile_if_tokens_available && | 90 if (start_reconcile_if_tokens_available && |
233 token_service_->GetAccounts().size() > 0) { | 91 token_service_->GetAccounts().size() > 0) { |
234 StartReconcile(); | 92 StartReconcile(); |
235 } | 93 } |
236 } | 94 } |
237 } | 95 } |
238 | 96 |
239 void AccountReconcilor::Shutdown() { | 97 void AccountReconcilor::Shutdown() { |
240 VLOG(1) << "AccountReconcilor::Shutdown"; | 98 VLOG(1) << "AccountReconcilor::Shutdown"; |
241 merge_session_helper_.CancelAll(); | 99 merge_session_helper_.CancelAll(); |
242 merge_session_helper_.RemoveObserver(this); | 100 merge_session_helper_.RemoveObserver(this); |
243 gaia_fetcher_.reset(); | 101 gaia_fetcher_.reset(); |
244 get_gaia_accounts_callbacks_.clear(); | 102 get_gaia_accounts_callbacks_.clear(); |
245 DeleteFetchers(); | |
246 UnregisterWithSigninManager(); | 103 UnregisterWithSigninManager(); |
247 UnregisterWithTokenService(); | 104 UnregisterWithTokenService(); |
248 UnregisterForCookieChanges(); | 105 UnregisterForCookieChanges(); |
249 } | 106 } |
250 | 107 |
251 void AccountReconcilor::AddMergeSessionObserver( | 108 void AccountReconcilor::AddMergeSessionObserver( |
252 MergeSessionHelper::Observer* observer) { | 109 MergeSessionHelper::Observer* observer) { |
253 merge_session_helper_.AddObserver(observer); | 110 merge_session_helper_.AddObserver(observer); |
254 } | 111 } |
255 | 112 |
256 void AccountReconcilor::RemoveMergeSessionObserver( | 113 void AccountReconcilor::RemoveMergeSessionObserver( |
257 MergeSessionHelper::Observer* observer) { | 114 MergeSessionHelper::Observer* observer) { |
258 merge_session_helper_.RemoveObserver(observer); | 115 merge_session_helper_.RemoveObserver(observer); |
259 } | 116 } |
260 | 117 |
261 void AccountReconcilor::DeleteFetchers() { | |
262 delete[] requests_; | |
263 requests_ = NULL; | |
264 | |
265 user_id_fetchers_.clear(); | |
266 refresh_token_fetchers_.clear(); | |
267 } | |
268 | |
269 bool AccountReconcilor::AreAllRefreshTokensChecked() const { | |
270 return chrome_accounts_.size() == | |
271 (valid_chrome_accounts_.size() + invalid_chrome_accounts_.size()); | |
272 } | |
273 | |
274 void AccountReconcilor::RegisterForCookieChanges() { | 118 void AccountReconcilor::RegisterForCookieChanges() { |
275 // First clear any existing registration to avoid DCHECKs that can otherwise | 119 // First clear any existing registration to avoid DCHECKs that can otherwise |
276 // go off in some embedders on reauth (e.g., ChromeSigninClient). | 120 // go off in some embedders on reauth (e.g., ChromeSigninClient). |
277 UnregisterForCookieChanges(); | 121 UnregisterForCookieChanges(); |
278 cookie_changed_subscription_ = client_->AddCookieChangedCallback( | 122 cookie_changed_subscription_ = client_->AddCookieChangedCallback( |
279 base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this))); | 123 base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this))); |
280 } | 124 } |
281 | 125 |
282 void AccountReconcilor::UnregisterForCookieChanges() { | 126 void AccountReconcilor::UnregisterForCookieChanges() { |
283 cookie_changed_subscription_.reset(); | 127 cookie_changed_subscription_.reset(); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
325 if (!token_service_->GetAccounts().size()) { | 169 if (!token_service_->GetAccounts().size()) { |
326 VLOG(1) << "AccountReconcilor::OnCookieChanged: cookie change is ingored" | 170 VLOG(1) << "AccountReconcilor::OnCookieChanged: cookie change is ingored" |
327 "because O2RT is not available yet."; | 171 "because O2RT is not available yet."; |
328 return; | 172 return; |
329 } | 173 } |
330 | 174 |
331 StartReconcile(); | 175 StartReconcile(); |
332 } | 176 } |
333 } | 177 } |
334 | 178 |
335 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { | |
336 VLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id; | |
337 PerformStartRemoveAction(account_id); | |
338 } | |
339 | |
340 void AccountReconcilor::OnEndBatchChanges() { | 179 void AccountReconcilor::OnEndBatchChanges() { |
341 VLOG(1) << "AccountReconcilor::OnEndBatchChanges"; | 180 VLOG(1) << "AccountReconcilor::OnEndBatchChanges"; |
342 StartReconcile(); | 181 StartReconcile(); |
343 } | 182 } |
344 | 183 |
345 void AccountReconcilor::GoogleSigninSucceeded(const std::string& account_id, | 184 void AccountReconcilor::GoogleSigninSucceeded(const std::string& account_id, |
346 const std::string& username, | 185 const std::string& username, |
347 const std::string& password) { | 186 const std::string& password) { |
348 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in"; | 187 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in"; |
349 RegisterForCookieChanges(); | 188 RegisterForCookieChanges(); |
(...skipping 13 matching lines...) Expand all Loading... |
363 | 202 |
364 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { | 203 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { |
365 if (!switches::IsEnableAccountConsistency()) { | 204 if (!switches::IsEnableAccountConsistency()) { |
366 MarkAccountAsAddedToCookie(account_id); | 205 MarkAccountAsAddedToCookie(account_id); |
367 return; | 206 return; |
368 } | 207 } |
369 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id; | 208 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id; |
370 merge_session_helper_.LogIn(account_id); | 209 merge_session_helper_.LogIn(account_id); |
371 } | 210 } |
372 | 211 |
373 void AccountReconcilor::PerformStartRemoveAction( | |
374 const std::string& account_id) { | |
375 VLOG(1) << "AccountReconcilor::PerformStartRemoveAction: " << account_id; | |
376 GetAccountsFromCookie(base::Bind( | |
377 &AccountReconcilor::PerformFinishRemoveAction, | |
378 base::Unretained(this), | |
379 account_id)); | |
380 } | |
381 | |
382 void AccountReconcilor::PerformFinishRemoveAction( | |
383 const std::string& account_id, | |
384 const GoogleServiceAuthError& error, | |
385 const std::vector<std::pair<std::string, bool> >& accounts) { | |
386 if (!switches::IsEnableAccountConsistency()) | |
387 return; | |
388 VLOG(1) << "AccountReconcilor::PerformFinishRemoveAction:" | |
389 << " account=" << account_id << " error=" << error.ToString(); | |
390 if (error.state() == GoogleServiceAuthError::NONE) { | |
391 AbortReconcile(); | |
392 std::vector<std::string> accounts_only; | |
393 for (std::vector<std::pair<std::string, bool> >::const_iterator i = | |
394 accounts.begin(); | |
395 i != accounts.end(); | |
396 ++i) { | |
397 accounts_only.push_back(i->first); | |
398 } | |
399 merge_session_helper_.LogOut(account_id, accounts_only); | |
400 } | |
401 // Wait for the next ReconcileAction if there is an error. | |
402 } | |
403 | |
404 void AccountReconcilor::PerformAddToChromeAction( | |
405 const std::string& account_id, | |
406 int session_index, | |
407 const std::string& signin_scoped_device_id) { | |
408 if (!switches::IsEnableAccountConsistency()) { | |
409 MarkAccountAsAddedToChrome(account_id); | |
410 return; | |
411 } | |
412 VLOG(1) << "AccountReconcilor::PerformAddToChromeAction:" | |
413 << " account=" << account_id << " session_index=" << session_index; | |
414 | |
415 #if !defined(OS_ANDROID) && !defined(OS_IOS) | |
416 refresh_token_fetchers_.push_back(new RefreshTokenFetcher( | |
417 this, account_id, session_index, signin_scoped_device_id)); | |
418 #endif | |
419 } | |
420 | |
421 void AccountReconcilor::PerformLogoutAllAccountsAction() { | 212 void AccountReconcilor::PerformLogoutAllAccountsAction() { |
422 if (!switches::IsEnableAccountConsistency()) | 213 if (!switches::IsEnableAccountConsistency()) |
423 return; | 214 return; |
424 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction"; | 215 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction"; |
425 merge_session_helper_.LogOutAllAccounts(); | 216 merge_session_helper_.LogOutAllAccounts(); |
426 } | 217 } |
427 | 218 |
428 void AccountReconcilor::StartReconcile() { | 219 void AccountReconcilor::StartReconcile() { |
429 if (!IsProfileConnected() || is_reconcile_started_ || | 220 if (!IsProfileConnected() || is_reconcile_started_ || |
430 get_gaia_accounts_callbacks_.size() > 0 || | 221 get_gaia_accounts_callbacks_.size() > 0 || |
431 merge_session_helper_.is_running()) | 222 merge_session_helper_.is_running()) |
432 return; | 223 return; |
433 | 224 |
434 is_reconcile_started_ = true; | 225 is_reconcile_started_ = true; |
435 | 226 |
436 StartFetchingExternalCcResult(); | 227 StartFetchingExternalCcResult(); |
437 | 228 |
438 // Reset state for validating gaia cookie. | 229 // Reset state for validating gaia cookie. |
439 are_gaia_accounts_set_ = false; | 230 are_gaia_accounts_set_ = false; |
440 gaia_accounts_.clear(); | 231 gaia_accounts_.clear(); |
441 GetAccountsFromCookie(base::Bind( | 232 GetAccountsFromCookie(base::Bind( |
442 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts, | 233 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts, |
443 base::Unretained(this))); | 234 base::Unretained(this))); |
444 | 235 |
445 // Reset state for validating oauth2 tokens. | 236 // Reset state for validating oauth2 tokens. |
446 primary_account_.clear(); | 237 primary_account_.clear(); |
447 chrome_accounts_.clear(); | 238 chrome_accounts_.clear(); |
448 DeleteFetchers(); | |
449 valid_chrome_accounts_.clear(); | |
450 invalid_chrome_accounts_.clear(); | |
451 add_to_cookie_.clear(); | 239 add_to_cookie_.clear(); |
452 add_to_chrome_.clear(); | |
453 ValidateAccountsFromTokenService(); | 240 ValidateAccountsFromTokenService(); |
454 } | 241 } |
455 | 242 |
456 void AccountReconcilor::GetAccountsFromCookie( | 243 void AccountReconcilor::GetAccountsFromCookie( |
457 GetAccountsFromCookieCallback callback) { | 244 GetAccountsFromCookieCallback callback) { |
458 get_gaia_accounts_callbacks_.push_back(callback); | 245 get_gaia_accounts_callbacks_.push_back(callback); |
459 if (!gaia_fetcher_) { | 246 if (!gaia_fetcher_) { |
460 // There is no list account request in flight. | 247 // There is no list account request in flight. |
461 gaia_fetcher_.reset(new GaiaAuthFetcher( | 248 gaia_fetcher_.reset(new GaiaAuthFetcher( |
462 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext())); | 249 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext())); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 | 321 |
535 void AccountReconcilor::ValidateAccountsFromTokenService() { | 322 void AccountReconcilor::ValidateAccountsFromTokenService() { |
536 primary_account_ = signin_manager_->GetAuthenticatedAccountId(); | 323 primary_account_ = signin_manager_->GetAuthenticatedAccountId(); |
537 DCHECK(!primary_account_.empty()); | 324 DCHECK(!primary_account_.empty()); |
538 | 325 |
539 chrome_accounts_ = token_service_->GetAccounts(); | 326 chrome_accounts_ = token_service_->GetAccounts(); |
540 | 327 |
541 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " | 328 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " |
542 << "Chrome " << chrome_accounts_.size() << " accounts, " | 329 << "Chrome " << chrome_accounts_.size() << " accounts, " |
543 << "Primary is '" << primary_account_ << "'"; | 330 << "Primary is '" << primary_account_ << "'"; |
544 | |
545 DCHECK(!requests_); | |
546 requests_ = | |
547 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()]; | |
548 const OAuth2TokenService::ScopeSet scopes = | |
549 AccountReconcilor::UserIdFetcher::GetScopes(); | |
550 for (size_t i = 0; i < chrome_accounts_.size(); ++i) { | |
551 requests_[i] = | |
552 token_service_->StartRequest(chrome_accounts_[i], scopes, this); | |
553 } | |
554 | |
555 DCHECK_EQ(0u, user_id_fetchers_.size()); | |
556 user_id_fetchers_.resize(chrome_accounts_.size()); | |
557 } | |
558 | |
559 void AccountReconcilor::OnGetTokenSuccess( | |
560 const OAuth2TokenService::Request* request, | |
561 const std::string& access_token, | |
562 const base::Time& expiration_time) { | |
563 size_t index; | |
564 for (index = 0; index < chrome_accounts_.size(); ++index) { | |
565 if (request == requests_[index].get()) | |
566 break; | |
567 } | |
568 DCHECK(index < chrome_accounts_.size()); | |
569 | |
570 const std::string& account_id = chrome_accounts_[index]; | |
571 | |
572 VLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " << account_id; | |
573 | |
574 DCHECK(!user_id_fetchers_[index]); | |
575 user_id_fetchers_[index] = new UserIdFetcher(this, access_token, account_id); | |
576 } | |
577 | |
578 void AccountReconcilor::OnGetTokenFailure( | |
579 const OAuth2TokenService::Request* request, | |
580 const GoogleServiceAuthError& error) { | |
581 size_t index; | |
582 for (index = 0; index < chrome_accounts_.size(); ++index) { | |
583 if (request == requests_[index].get()) | |
584 break; | |
585 } | |
586 DCHECK(index < chrome_accounts_.size()); | |
587 | |
588 const std::string& account_id = chrome_accounts_[index]; | |
589 | |
590 VLOG(1) << "AccountReconcilor::OnGetTokenFailure: invalid " << account_id; | |
591 HandleFailedAccountIdCheck(account_id); | |
592 } | 331 } |
593 | 332 |
594 void AccountReconcilor::OnNewProfileManagementFlagChanged( | 333 void AccountReconcilor::OnNewProfileManagementFlagChanged( |
595 bool new_flag_status) { | 334 bool new_flag_status) { |
596 if (new_flag_status) { | 335 if (new_flag_status) { |
597 // The reconciler may have been newly created just before this call, or may | 336 // The reconciler may have been newly created just before this call, or may |
598 // have already existed and in mid-reconcile. To err on the safe side, force | 337 // have already existed and in mid-reconcile. To err on the safe side, force |
599 // a restart. | 338 // a restart. |
600 Shutdown(); | 339 Shutdown(); |
601 Initialize(true); | 340 Initialize(true); |
602 } else { | 341 } else { |
603 Shutdown(); | 342 Shutdown(); |
604 } | 343 } |
605 } | 344 } |
606 | 345 |
607 void AccountReconcilor::FinishReconcile() { | 346 void AccountReconcilor::FinishReconcile() { |
608 // Make sure that the process of validating the gaia cookie and the oauth2 | |
609 // tokens individually is done before proceeding with reconciliation. | |
610 if (!are_gaia_accounts_set_ || !AreAllRefreshTokensChecked()) | |
611 return; | |
612 | |
613 VLOG(1) << "AccountReconcilor::FinishReconcile"; | 347 VLOG(1) << "AccountReconcilor::FinishReconcile"; |
614 | 348 DCHECK(are_gaia_accounts_set_); |
615 DeleteFetchers(); | |
616 | |
617 DCHECK(add_to_cookie_.empty()); | 349 DCHECK(add_to_cookie_.empty()); |
618 DCHECK(add_to_chrome_.empty()); | |
619 int number_gaia_accounts = gaia_accounts_.size(); | 350 int number_gaia_accounts = gaia_accounts_.size(); |
620 bool are_primaries_equal = | 351 bool are_primaries_equal = number_gaia_accounts > 0 && |
621 number_gaia_accounts > 0 && | |
622 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first); | 352 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first); |
623 | 353 |
| 354 // If there are any accounts in the gaia cookie but not in chrome, then |
| 355 // those accounts need to be removed from the cookie. This means we need |
| 356 // to blow the cookie away. |
| 357 int removed_from_cookie = 0; |
| 358 for (size_t i = 0; i < gaia_accounts_.size(); ++i) { |
| 359 const std::string& gaia_account = gaia_accounts_[i].first; |
| 360 if (gaia_accounts_[i].second && |
| 361 chrome_accounts_.end() == |
| 362 std::find_if(chrome_accounts_.begin(), |
| 363 chrome_accounts_.end(), |
| 364 std::bind1st(AreEmailsSameFunc(), gaia_account))) { |
| 365 ++removed_from_cookie; |
| 366 } |
| 367 } |
624 | 368 |
625 if (are_primaries_equal) { | 369 bool rebuild_cookie = !are_primaries_equal || removed_from_cookie > 0; |
626 // Determine if we need to merge accounts from gaia cookie to chrome. | 370 std::vector<std::pair<std::string, bool> > original_gaia_accounts = |
627 for (size_t i = 0; i < gaia_accounts_.size(); ++i) { | 371 gaia_accounts_; |
628 const std::string& gaia_account = gaia_accounts_[i].first; | 372 if (rebuild_cookie) { |
629 if (gaia_accounts_[i].second && | |
630 valid_chrome_accounts_.find(gaia_account) == | |
631 valid_chrome_accounts_.end()) { | |
632 add_to_chrome_.push_back(std::make_pair(gaia_account, i)); | |
633 } | |
634 } | |
635 } else { | |
636 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie"; | 373 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie"; |
637 // Really messed up state. Blow away the gaia cookie completely and | 374 // Really messed up state. Blow away the gaia cookie completely and |
638 // rebuild it, making sure the primary account as specified by the | 375 // rebuild it, making sure the primary account as specified by the |
639 // SigninManager is the first session in the gaia cookie. | 376 // SigninManager is the first session in the gaia cookie. |
640 PerformLogoutAllAccountsAction(); | 377 PerformLogoutAllAccountsAction(); |
641 gaia_accounts_.clear(); | 378 gaia_accounts_.clear(); |
642 } | 379 } |
643 | 380 |
644 // Create a list of accounts that need to be added to the gaia cookie. | 381 // Create a list of accounts that need to be added to the gaia cookie. |
645 // The primary account must be first to make sure it becomes the default | 382 // The primary account must be first to make sure it becomes the default |
646 // account in the case where chrome is completely rebuilding the cookie. | 383 // account in the case where chrome is completely rebuilding the cookie. |
647 add_to_cookie_.push_back(primary_account_); | 384 add_to_cookie_.push_back(primary_account_); |
648 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin(); | 385 for (size_t i = 0; i < chrome_accounts_.size(); ++i) { |
649 i != valid_chrome_accounts_.end(); | 386 if (chrome_accounts_[i] != primary_account_) |
650 ++i) { | 387 add_to_cookie_.push_back(chrome_accounts_[i]); |
651 if (*i != primary_account_) | |
652 add_to_cookie_.push_back(*i); | |
653 } | 388 } |
654 | 389 |
655 // For each account known to chrome, PerformMergeAction() if the account is | 390 // For each account known to chrome, PerformMergeAction() if the account is |
656 // not already in the cookie jar or its state is invalid, or signal merge | 391 // not already in the cookie jar or its state is invalid, or signal merge |
657 // completed otherwise. Make a copy of |add_to_cookie_| since calls to | 392 // completed otherwise. Make a copy of |add_to_cookie_| since calls to |
658 // SignalComplete() will change the array. | 393 // SignalComplete() will change the array. |
659 std::vector<std::string> add_to_cookie_copy = add_to_cookie_; | 394 std::vector<std::string> add_to_cookie_copy = add_to_cookie_; |
660 int added_to_cookie = 0; | 395 int added_to_cookie = 0; |
661 bool external_cc_result_completed = | 396 bool external_cc_result_completed = |
662 !merge_session_helper_.StillFetchingExternalCcResult(); | 397 !merge_session_helper_.StillFetchingExternalCcResult(); |
663 for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) { | 398 for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) { |
664 if (gaia_accounts_.end() != | 399 if (gaia_accounts_.end() != |
665 std::find_if(gaia_accounts_.begin(), | 400 std::find_if(gaia_accounts_.begin(), |
666 gaia_accounts_.end(), | 401 gaia_accounts_.end(), |
667 std::bind1st(EmailEqualToFunc(), | 402 std::bind1st(EmailEqualToFunc(), |
668 std::make_pair(add_to_cookie_copy[i], | 403 std::make_pair(add_to_cookie_copy[i], |
669 true)))) { | 404 true)))) { |
670 merge_session_helper_.SignalComplete( | 405 merge_session_helper_.SignalComplete( |
671 add_to_cookie_copy[i], | 406 add_to_cookie_copy[i], |
672 GoogleServiceAuthError::AuthErrorNone()); | 407 GoogleServiceAuthError::AuthErrorNone()); |
673 } else { | 408 } else { |
674 PerformMergeAction(add_to_cookie_copy[i]); | 409 PerformMergeAction(add_to_cookie_copy[i]); |
675 added_to_cookie++; | 410 if (original_gaia_accounts.end() == |
| 411 std::find_if(original_gaia_accounts.begin(), |
| 412 original_gaia_accounts.end(), |
| 413 std::bind1st(EmailEqualToFunc(), |
| 414 std::make_pair(add_to_cookie_copy[i], |
| 415 true)))) { |
| 416 added_to_cookie++; |
| 417 } |
676 } | 418 } |
677 } | 419 } |
678 | 420 |
679 // Log whether the external connection checks were completed when we tried | 421 // Log whether the external connection checks were completed when we tried |
680 // to add the accounts to the cookie. | 422 // to add the accounts to the cookie. |
681 if (added_to_cookie > 0) | 423 if (rebuild_cookie || added_to_cookie > 0) |
682 signin_metrics::LogExternalCcResultFetches(external_cc_result_completed); | 424 signin_metrics::LogExternalCcResultFetches(external_cc_result_completed); |
683 | 425 |
684 std::string signin_scoped_device_id = client_->GetSigninScopedDeviceId(); | 426 signin_metrics::LogSigninAccountReconciliation(chrome_accounts_.size(), |
685 // For each account in the gaia cookie not known to chrome, | |
686 // PerformAddToChromeAction. Make a copy of |add_to_chrome| since calls to | |
687 // PerformAddToChromeAction() may modify this array. | |
688 std::vector<std::pair<std::string, int> > add_to_chrome_copy = add_to_chrome_; | |
689 for (std::vector<std::pair<std::string, int> >::const_iterator i = | |
690 add_to_chrome_copy.begin(); | |
691 i != add_to_chrome_copy.end(); | |
692 ++i) { | |
693 PerformAddToChromeAction(i->first, i->second, signin_scoped_device_id); | |
694 } | |
695 | |
696 signin_metrics::LogSigninAccountReconciliation(valid_chrome_accounts_.size(), | |
697 added_to_cookie, | 427 added_to_cookie, |
698 add_to_chrome_.size(), | 428 removed_from_cookie, |
699 are_primaries_equal, | 429 are_primaries_equal, |
700 first_execution_, | 430 first_execution_, |
701 number_gaia_accounts); | 431 number_gaia_accounts); |
702 first_execution_ = false; | 432 first_execution_ = false; |
703 CalculateIfReconcileIsDone(); | 433 CalculateIfReconcileIsDone(); |
704 ScheduleStartReconcileIfChromeAccountsChanged(); | 434 ScheduleStartReconcileIfChromeAccountsChanged(); |
705 } | 435 } |
706 | 436 |
707 void AccountReconcilor::AbortReconcile() { | 437 void AccountReconcilor::AbortReconcile() { |
708 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later"; | 438 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later"; |
709 DeleteFetchers(); | |
710 add_to_cookie_.clear(); | 439 add_to_cookie_.clear(); |
711 add_to_chrome_.clear(); | |
712 CalculateIfReconcileIsDone(); | 440 CalculateIfReconcileIsDone(); |
713 } | 441 } |
714 | 442 |
715 void AccountReconcilor::CalculateIfReconcileIsDone() { | 443 void AccountReconcilor::CalculateIfReconcileIsDone() { |
716 is_reconcile_started_ = !add_to_cookie_.empty() || !add_to_chrome_.empty(); | 444 is_reconcile_started_ = !add_to_cookie_.empty(); |
717 if (!is_reconcile_started_) | 445 if (!is_reconcile_started_) |
718 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done"; | 446 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done"; |
719 } | 447 } |
720 | 448 |
721 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { | 449 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { |
722 if (is_reconcile_started_) | 450 if (is_reconcile_started_) |
723 return; | 451 return; |
724 | 452 |
725 // Start a reconcile as the token accounts have changed. | 453 // Start a reconcile as the token accounts have changed. |
726 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged"; | 454 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged"; |
(...skipping 19 matching lines...) Expand all Loading... |
746 return true; | 474 return true; |
747 } | 475 } |
748 } | 476 } |
749 return false; | 477 return false; |
750 } | 478 } |
751 | 479 |
752 void AccountReconcilor::MergeSessionCompleted( | 480 void AccountReconcilor::MergeSessionCompleted( |
753 const std::string& account_id, | 481 const std::string& account_id, |
754 const GoogleServiceAuthError& error) { | 482 const GoogleServiceAuthError& error) { |
755 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id=" | 483 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id=" |
756 << account_id; | 484 << account_id << " error=" << error.ToString(); |
757 | 485 |
758 if (MarkAccountAsAddedToCookie(account_id)) { | 486 if (MarkAccountAsAddedToCookie(account_id)) { |
759 CalculateIfReconcileIsDone(); | 487 CalculateIfReconcileIsDone(); |
760 ScheduleStartReconcileIfChromeAccountsChanged(); | 488 ScheduleStartReconcileIfChromeAccountsChanged(); |
761 } | 489 } |
762 } | 490 } |
763 | |
764 void AccountReconcilor::HandleSuccessfulAccountIdCheck( | |
765 const std::string& account_id) { | |
766 valid_chrome_accounts_.insert(account_id); | |
767 FinishReconcile(); | |
768 } | |
769 | |
770 void AccountReconcilor::HandleFailedAccountIdCheck( | |
771 const std::string& account_id) { | |
772 invalid_chrome_accounts_.insert(account_id); | |
773 FinishReconcile(); | |
774 } | |
775 | |
776 void AccountReconcilor::PerformAddAccountToTokenService( | |
777 const std::string& account_id, | |
778 const std::string& refresh_token) { | |
779 // The flow should never get to this method if new_profile_management is | |
780 // false, but better safe than sorry. | |
781 if (!switches::IsEnableAccountConsistency()) | |
782 return; | |
783 token_service_->UpdateCredentials(account_id, refresh_token); | |
784 } | |
785 | |
786 // Remove the account from the list that is being updated. | |
787 void AccountReconcilor::MarkAccountAsAddedToChrome( | |
788 const std::string& account_id) { | |
789 for (std::vector<std::pair<std::string, int> >::iterator i = | |
790 add_to_chrome_.begin(); | |
791 i != add_to_chrome_.end(); | |
792 ++i) { | |
793 if (gaia::AreEmailsSame(account_id, i->first)) { | |
794 add_to_chrome_.erase(i); | |
795 break; | |
796 } | |
797 } | |
798 } | |
799 | |
800 void AccountReconcilor::HandleRefreshTokenFetched( | |
801 const std::string& account_id, | |
802 const std::string& refresh_token) { | |
803 if (!refresh_token.empty()) | |
804 PerformAddAccountToTokenService(account_id, refresh_token); | |
805 | |
806 MarkAccountAsAddedToChrome(account_id); | |
807 CalculateIfReconcileIsDone(); | |
808 } | |
OLD | NEW |