Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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 "base/json/json_reader.h" | |
| 5 #include "base/logging.h" | 6 #include "base/logging.h" |
| 6 #include "base/time/time.h" | 7 #include "base/time/time.h" |
| 7 #include "chrome/browser/chrome_notification_types.h" | 8 #include "chrome/browser/chrome_notification_types.h" |
| 8 #include "chrome/browser/net/chrome_cookie_notification_details.h" | 9 #include "chrome/browser/net/chrome_cookie_notification_details.h" |
| 9 #include "chrome/browser/profiles/profile.h" | 10 #include "chrome/browser/profiles/profile.h" |
| 10 #include "chrome/browser/signin/account_reconcilor.h" | 11 #include "chrome/browser/signin/account_reconcilor.h" |
| 11 #include "chrome/browser/signin/google_auto_login_helper.h" | 12 #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.h" |
| 13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" | 14 #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.h" |
| 15 #include "chrome/browser/signin/signin_manager_factory.h" | 16 #include "chrome/browser/signin/signin_manager_factory.h" |
| 16 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
| 17 #include "content/public/browser/notification_details.h" | 18 #include "content/public/browser/notification_details.h" |
| 18 #include "content/public/browser/notification_source.h" | 19 #include "content/public/browser/notification_source.h" |
| 20 #include "google_apis/gaia/gaia_auth_fetcher.h" | |
| 21 #include "google_apis/gaia/gaia_constants.h" | |
| 19 | 22 |
| 20 AccountReconcilor::AccountReconcilor(Profile* profile) : profile_(profile) { | 23 AccountReconcilor::AccountReconcilor(Profile* profile) |
| 24 : profile_(profile), | |
| 25 are_gaia_accounts_set_(false), | |
| 26 requests_(NULL) { | |
| 21 RegisterWithSigninManager(); | 27 RegisterWithSigninManager(); |
| 22 RegisterWithCookieMonster(); | 28 RegisterWithCookieMonster(); |
| 23 | 29 |
| 24 // If this profile is not connected, the reconcilor should do nothing but | 30 // If this profile is not connected, the reconcilor should do nothing but |
| 25 // wait for the connection. | 31 // wait for the connection. |
| 26 SigninManagerBase* signin_manager = | 32 if (IsProfileConnected()) { |
| 27 SigninManagerFactory::GetForProfile(profile_); | |
| 28 if (!signin_manager->GetAuthenticatedUsername().empty()) { | |
| 29 RegisterWithTokenService(); | 33 RegisterWithTokenService(); |
| 30 StartPeriodicReconciliation(); | 34 StartPeriodicReconciliation(); |
| 31 } | 35 } |
| 32 } | 36 } |
| 33 | 37 |
| 38 AccountReconcilor::~AccountReconcilor() { | |
| 39 // Make sure shutdown was called first. | |
| 40 DCHECK(registrar_.IsEmpty()); | |
| 41 DCHECK(!reconciliation_timer_.IsRunning()); | |
| 42 DCHECK(!requests_); | |
| 43 } | |
| 44 | |
| 45 void AccountReconcilor::Shutdown() { | |
| 46 DVLOG(1) << "AccountReconcilor::Shutdown"; | |
| 47 DeleteAccessTokenRequests(); | |
| 48 UnregisterWithSigninManager(); | |
| 49 UnregisterWithTokenService(); | |
| 50 UnregisterWithCookieMonster(); | |
| 51 StopPeriodicReconciliation(); | |
| 52 } | |
| 53 | |
| 54 void AccountReconcilor::DeleteAccessTokenRequests() { | |
| 55 delete[] requests_; | |
| 56 requests_ = NULL; | |
| 57 } | |
| 58 | |
| 34 void AccountReconcilor::RegisterWithCookieMonster() { | 59 void AccountReconcilor::RegisterWithCookieMonster() { |
| 35 content::Source<Profile> source(profile_); | 60 content::Source<Profile> source(profile_); |
| 36 registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); | 61 registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); |
| 37 } | 62 } |
| 38 | 63 |
| 39 void AccountReconcilor::UnregisterWithCookieMonster() { | 64 void AccountReconcilor::UnregisterWithCookieMonster() { |
| 40 content::Source<Profile> source(profile_); | 65 content::Source<Profile> source(profile_); |
| 41 registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); | 66 registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source); |
| 42 } | 67 } |
| 43 | 68 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 59 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); | 84 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| 60 token_service->AddObserver(this); | 85 token_service->AddObserver(this); |
| 61 } | 86 } |
| 62 | 87 |
| 63 void AccountReconcilor::UnregisterWithTokenService() { | 88 void AccountReconcilor::UnregisterWithTokenService() { |
| 64 ProfileOAuth2TokenService* token_service = | 89 ProfileOAuth2TokenService* token_service = |
| 65 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); | 90 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| 66 token_service->RemoveObserver(this); | 91 token_service->RemoveObserver(this); |
| 67 } | 92 } |
| 68 | 93 |
| 94 bool AccountReconcilor::IsProfileConnected() { | |
| 95 return !SigninManagerFactory::GetForProfile(profile_)-> | |
| 96 GetAuthenticatedUsername().empty(); | |
| 97 } | |
| 98 | |
| 69 void AccountReconcilor::StartPeriodicReconciliation() { | 99 void AccountReconcilor::StartPeriodicReconciliation() { |
| 100 DVLOG(1) << "AccountReconcilor::StartPeriodicReconciliation"; | |
| 70 // TODO(rogerta): pick appropriate thread and timeout value. | 101 // TODO(rogerta): pick appropriate thread and timeout value. |
| 71 reconciliation_timer_.Start( | 102 reconciliation_timer_.Start( |
| 72 FROM_HERE, | 103 FROM_HERE, |
| 73 base::TimeDelta::FromMinutes(5), | 104 base::TimeDelta::FromSeconds(300), |
| 74 this, | 105 this, |
| 75 &AccountReconcilor::PeriodicReconciliation); | 106 &AccountReconcilor::PeriodicReconciliation); |
| 76 } | 107 } |
| 77 | 108 |
| 78 void AccountReconcilor::StopPeriodicReconciliation() { | 109 void AccountReconcilor::StopPeriodicReconciliation() { |
| 110 DVLOG(1) << "AccountReconcilor::StopPeriodicReconciliation"; | |
| 79 reconciliation_timer_.Stop(); | 111 reconciliation_timer_.Stop(); |
| 80 } | 112 } |
| 81 | 113 |
| 82 void AccountReconcilor::PeriodicReconciliation() { | 114 void AccountReconcilor::PeriodicReconciliation() { |
| 83 PerformReconcileAction(); | 115 DVLOG(1) << "AccountReconcilor::PeriodicReconciliation"; |
| 116 StartReconcileAction(); | |
| 84 } | 117 } |
| 85 | 118 |
| 86 void AccountReconcilor::Observe(int type, | 119 void AccountReconcilor::Observe(int type, |
| 87 const content::NotificationSource& source, | 120 const content::NotificationSource& source, |
| 88 const content::NotificationDetails& details) { | 121 const content::NotificationDetails& details) { |
| 89 switch (type) { | 122 switch (type) { |
| 90 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: | 123 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: |
| 124 DVLOG(1) << "AccountReconcilor::Observe: signed in"; | |
| 91 RegisterWithTokenService(); | 125 RegisterWithTokenService(); |
| 92 StartPeriodicReconciliation(); | 126 StartPeriodicReconciliation(); |
| 93 break; | 127 break; |
| 94 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: | 128 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: |
| 129 DVLOG(1) << "AccountReconcilor::Observe: signed out"; | |
| 95 UnregisterWithTokenService(); | 130 UnregisterWithTokenService(); |
| 96 StopPeriodicReconciliation(); | 131 StopPeriodicReconciliation(); |
| 97 break; | 132 break; |
| 98 case chrome::NOTIFICATION_COOKIE_CHANGED: | 133 case chrome::NOTIFICATION_COOKIE_CHANGED: |
| 134 DVLOG(1) << "AccountReconcilor::Observe: cookies changed"; | |
|
acleung1
2013/11/04 21:19:00
This might be too verbose. We should probably LOG
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
Agreed. Removed for now.
| |
| 99 OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); | 135 OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); |
| 100 break; | 136 break; |
| 101 default: | 137 default: |
| 102 NOTREACHED(); | 138 NOTREACHED(); |
| 103 break; | 139 break; |
| 104 } | 140 } |
| 105 } | 141 } |
| 106 | 142 |
| 107 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) { | 143 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) { |
| 108 // TODO(acleung): Filter out cookies by looking at the domain. | 144 // TODO(acleung): Filter out cookies by looking at the domain. |
| 109 // PerformReconcileAction(); | 145 // StartReconcileAction(); |
| 110 } | 146 } |
| 111 | 147 |
| 112 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) { | 148 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) { |
| 149 DVLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id; | |
| 113 PerformMergeAction(account_id); | 150 PerformMergeAction(account_id); |
| 114 } | 151 } |
| 115 | 152 |
| 116 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { | 153 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { |
| 154 DVLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id; | |
| 117 PerformRemoveAction(account_id); | 155 PerformRemoveAction(account_id); |
| 118 } | 156 } |
| 119 | 157 |
| 120 void AccountReconcilor::OnRefreshTokensLoaded() {} | 158 void AccountReconcilor::OnRefreshTokensLoaded() {} |
| 121 | 159 |
| 122 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { | 160 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { |
| 123 // GoogleAutoLoginHelper deletes itself upon success / failure. | 161 // GoogleAutoLoginHelper deletes itself upon success / failure. |
| 124 GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_); | 162 GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_); |
|
guohui
2013/11/04 22:44:16
another helper? we have a bunch of helpers to clea
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
:-) Yes lets do a pass on all our helpers and see
acleung1
2013/11/05 00:04:28
This is slightly misnamed. We used to use that for
| |
| 125 helper->LogIn(account_id); | 163 helper->LogIn(account_id); |
| 126 } | 164 } |
| 127 | 165 |
| 128 void AccountReconcilor::PerformRemoveAction(const std::string& account_id) { | 166 void AccountReconcilor::PerformRemoveAction(const std::string& account_id) { |
| 129 // TODO(acleung): Implement this: | 167 // TODO(acleung): Implement this: |
| 130 } | 168 } |
| 131 | 169 |
| 132 void AccountReconcilor::PerformReconcileAction() { | 170 void AccountReconcilor::StartReconcileAction() { |
| 133 // TODO(acleung): Implement this: | 171 if (!IsProfileConnected()) |
| 172 return; | |
| 173 | |
| 174 // Reset state for validating gaia cookie. | |
| 175 are_gaia_accounts_set_ = false; | |
| 176 gaia_accounts_.clear(); | |
| 177 GetAccountsFromCookie(); | |
| 178 | |
| 179 // Reset state for validating oauth2 tokens. | |
| 180 primary_account_.clear(); | |
| 181 chrome_accounts_.clear(); | |
| 182 DeleteAccessTokenRequests(); | |
| 183 valid_chrome_accounts_.clear(); | |
| 184 invalid_chrome_accounts_.clear(); | |
| 185 ValidateAccountsFromTokenService(); | |
| 134 } | 186 } |
| 135 | 187 |
| 136 AccountReconcilor::~AccountReconcilor() { | 188 void AccountReconcilor::GetAccountsFromCookie() { |
| 137 // Make sure shutdown was called first. | 189 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, |
| 138 DCHECK(registrar_.IsEmpty()); | 190 profile_->GetRequestContext())); |
| 191 gaia_fetcher_->StartListAccounts(); | |
| 139 } | 192 } |
| 140 | 193 |
| 141 void AccountReconcilor::Shutdown() { | 194 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) { |
| 142 UnregisterWithSigninManager(); | 195 gaia_fetcher_.reset(); |
| 143 UnregisterWithTokenService(); | 196 |
| 144 UnregisterWithCookieMonster(); | 197 // Get account information from response data. |
| 145 StopPeriodicReconciliation(); | 198 gaia_accounts_ = ParseListAccountsData(data); |
| 199 if (gaia_accounts_.size() > 0) { | |
| 200 DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: " | |
| 201 << "Gaia " << gaia_accounts_.size() << " accounts, " | |
| 202 << "Primary is '" << gaia_accounts_[0] << "'"; | |
| 203 } else { | |
| 204 DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts"; | |
| 205 } | |
| 206 | |
| 207 are_gaia_accounts_set_ = true; | |
| 208 FinishReconcileAction(); | |
| 146 } | 209 } |
| 210 | |
| 211 std::vector<std::string> AccountReconcilor::ParseListAccountsData( | |
|
guohui
2013/11/04 22:44:16
is it better to move the parsing code to GaiaAuthF
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
I'm not sure. Maybe leave it here for now until s
| |
| 212 const std::string& data) { | |
| 213 std::vector<std::string> account_ids; | |
| 214 | |
| 215 // Parse returned data and make sure we have data. | |
| 216 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); | |
|
acleung1
2013/11/04 21:19:00
What are the cases where we get no data? Should th
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
This would be an error. The gaia auth fetcher alr
| |
| 217 if (!value) | |
| 218 return account_ids; | |
| 219 | |
| 220 base::ListValue* list; | |
| 221 if (!value->GetAsList(&list) || list->GetSize() < 2) | |
| 222 return account_ids; | |
| 223 | |
| 224 // Get list of account info. | |
| 225 base::ListValue* accounts; | |
| 226 list->GetList(1, &accounts); | |
| 227 if (accounts == NULL) | |
| 228 return account_ids; | |
| 229 | |
| 230 // Build a vector of accounts from the cookie. Order is important: the first | |
| 231 // account in the list is the primary account. | |
| 232 for (size_t i = 0; i < accounts->GetSize(); ++i) { | |
| 233 base::ListValue* account; | |
| 234 accounts->GetList(i, &account); | |
| 235 if (accounts != NULL) { | |
| 236 std::string email; | |
| 237 account->GetString(3, &email); | |
| 238 if (!email.empty()) | |
| 239 account_ids.push_back(email); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 return account_ids; | |
| 244 } | |
| 245 | |
| 246 void AccountReconcilor::OnListAccountsFailure( | |
| 247 const GoogleServiceAuthError& error) { | |
| 248 gaia_fetcher_.reset(); | |
| 249 DVLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString(); | |
| 250 | |
| 251 are_gaia_accounts_set_ = true; | |
| 252 FinishReconcileAction(); | |
| 253 } | |
| 254 | |
| 255 void AccountReconcilor::ValidateAccountsFromTokenService() { | |
| 256 primary_account_ = | |
| 257 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername(); | |
| 258 DCHECK(!primary_account_.empty()); | |
| 259 | |
| 260 ProfileOAuth2TokenService* token_service = | |
| 261 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); | |
| 262 chrome_accounts_ = token_service->GetAccounts(); | |
| 263 DCHECK(chrome_accounts_.size() > 0); | |
|
guohui
2013/11/04 22:44:16
why is this a DCHECK? shouldn't we handle this jus
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
The AC is only enabled for connected profiles. Th
| |
| 264 | |
| 265 DVLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " | |
| 266 << "Chrome " << chrome_accounts_.size() << " accounts, " | |
| 267 << "Primary is '" << primary_account_ << "'"; | |
| 268 | |
| 269 DCHECK(!requests_); | |
| 270 requests_ = | |
| 271 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()]; | |
| 272 for (size_t i = 0; i < chrome_accounts_.size(); ++i) { | |
|
acleung1
2013/11/04 21:19:00
I am not entirely following this step.
Wouldn't w
Roger Tawa OOO till Jul 10th
2013/11/04 23:38:42
The O2TS may have an list of tokens, but they coul
| |
| 273 requests_[i] = token_service->StartRequest(chrome_accounts_[i], | |
| 274 OAuth2TokenService::ScopeSet(), | |
| 275 this); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 void AccountReconcilor::OnGetTokenSuccess( | |
| 280 const OAuth2TokenService::Request* request, | |
| 281 const std::string& access_token, | |
| 282 const base::Time& expiration_time) { | |
| 283 DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " | |
| 284 << request->GetAccountId(); | |
| 285 valid_chrome_accounts_.insert(request->GetAccountId()); | |
| 286 FinishReconcileAction(); | |
| 287 } | |
| 288 | |
| 289 void AccountReconcilor::OnGetTokenFailure( | |
| 290 const OAuth2TokenService::Request* request, | |
| 291 const GoogleServiceAuthError& error) { | |
| 292 DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: invalid " | |
| 293 << request->GetAccountId(); | |
| 294 invalid_chrome_accounts_.insert(request->GetAccountId()); | |
| 295 FinishReconcileAction(); | |
| 296 } | |
| 297 | |
| 298 void AccountReconcilor::FinishReconcileAction() { | |
| 299 // Make sure that the process of validating the gaia cookie and the oauth2 | |
| 300 // tokens individually is done before proceeding with reconciliation. | |
| 301 if (!are_gaia_accounts_set_ || | |
| 302 (chrome_accounts_.size() != (valid_chrome_accounts_.size() + | |
| 303 invalid_chrome_accounts_.size()))) { | |
| 304 return; | |
| 305 } | |
| 306 | |
| 307 DVLOG(1) << "AccountReconcilor::FinishReconcileAction"; | |
| 308 | |
| 309 bool are_primaries_equal = | |
| 310 gaia_accounts_.size() > 0 && primary_account_ == gaia_accounts_[0]; | |
| 311 bool have_same_accounts = chrome_accounts_.size() == gaia_accounts_.size(); | |
| 312 if (have_same_accounts) { | |
| 313 for (size_t i = 0; i < gaia_accounts_.size(); ++i) { | |
| 314 if (std::find(chrome_accounts_.begin(), chrome_accounts_.end(), | |
| 315 gaia_accounts_[i]) == chrome_accounts_.end()) { | |
| 316 have_same_accounts = false; | |
| 317 break; | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 if (!are_primaries_equal || !have_same_accounts) { | |
| 323 // TODO(rogerta): fix things up. | |
| 324 } | |
| 325 } | |
| OLD | NEW |