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: |
99 OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); | 134 OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr()); |
100 break; | 135 break; |
101 default: | 136 default: |
102 NOTREACHED(); | 137 NOTREACHED(); |
103 break; | 138 break; |
104 } | 139 } |
105 } | 140 } |
106 | 141 |
107 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) { | 142 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) { |
108 // TODO(acleung): Filter out cookies by looking at the domain. | 143 // TODO(acleung): Filter out cookies by looking at the domain. |
109 // PerformReconcileAction(); | 144 // StartReconcileAction(); |
110 } | 145 } |
111 | 146 |
112 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) { | 147 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) { |
| 148 DVLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id; |
113 PerformMergeAction(account_id); | 149 PerformMergeAction(account_id); |
114 } | 150 } |
115 | 151 |
116 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { | 152 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) { |
| 153 DVLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id; |
117 PerformRemoveAction(account_id); | 154 PerformRemoveAction(account_id); |
118 } | 155 } |
119 | 156 |
120 void AccountReconcilor::OnRefreshTokensLoaded() {} | 157 void AccountReconcilor::OnRefreshTokensLoaded() {} |
121 | 158 |
122 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { | 159 void AccountReconcilor::PerformMergeAction(const std::string& account_id) { |
123 // GoogleAutoLoginHelper deletes itself upon success / failure. | 160 // GoogleAutoLoginHelper deletes itself upon success / failure. |
124 GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_); | 161 GoogleAutoLoginHelper* helper = new GoogleAutoLoginHelper(profile_); |
125 helper->LogIn(account_id); | 162 helper->LogIn(account_id); |
126 } | 163 } |
127 | 164 |
128 void AccountReconcilor::PerformRemoveAction(const std::string& account_id) { | 165 void AccountReconcilor::PerformRemoveAction(const std::string& account_id) { |
129 // TODO(acleung): Implement this: | 166 // TODO(acleung): Implement this: |
130 } | 167 } |
131 | 168 |
132 void AccountReconcilor::PerformReconcileAction() { | 169 void AccountReconcilor::StartReconcileAction() { |
133 // TODO(acleung): Implement this: | 170 if (!IsProfileConnected()) |
| 171 return; |
| 172 |
| 173 // Reset state for validating gaia cookie. |
| 174 are_gaia_accounts_set_ = false; |
| 175 gaia_accounts_.clear(); |
| 176 GetAccountsFromCookie(); |
| 177 |
| 178 // Reset state for validating oauth2 tokens. |
| 179 primary_account_.clear(); |
| 180 chrome_accounts_.clear(); |
| 181 DeleteAccessTokenRequests(); |
| 182 valid_chrome_accounts_.clear(); |
| 183 invalid_chrome_accounts_.clear(); |
| 184 ValidateAccountsFromTokenService(); |
134 } | 185 } |
135 | 186 |
136 AccountReconcilor::~AccountReconcilor() { | 187 void AccountReconcilor::GetAccountsFromCookie() { |
137 // Make sure shutdown was called first. | 188 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, |
138 DCHECK(registrar_.IsEmpty()); | 189 profile_->GetRequestContext())); |
| 190 gaia_fetcher_->StartListAccounts(); |
139 } | 191 } |
140 | 192 |
141 void AccountReconcilor::Shutdown() { | 193 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) { |
142 UnregisterWithSigninManager(); | 194 gaia_fetcher_.reset(); |
143 UnregisterWithTokenService(); | 195 |
144 UnregisterWithCookieMonster(); | 196 // Get account information from response data. |
145 StopPeriodicReconciliation(); | 197 gaia_accounts_ = ParseListAccountsData(data); |
| 198 if (gaia_accounts_.size() > 0) { |
| 199 DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: " |
| 200 << "Gaia " << gaia_accounts_.size() << " accounts, " |
| 201 << "Primary is '" << gaia_accounts_[0] << "'"; |
| 202 } else { |
| 203 DVLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts"; |
| 204 } |
| 205 |
| 206 are_gaia_accounts_set_ = true; |
| 207 FinishReconcileAction(); |
146 } | 208 } |
| 209 |
| 210 // static |
| 211 std::vector<std::string> AccountReconcilor::ParseListAccountsData( |
| 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)); |
| 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 if (!list->GetList(1, &accounts) || accounts == NULL) |
| 227 return account_ids; |
| 228 |
| 229 // Build a vector of accounts from the cookie. Order is important: the first |
| 230 // account in the list is the primary account. |
| 231 for (size_t i = 0; i < accounts->GetSize(); ++i) { |
| 232 base::ListValue* account; |
| 233 if (accounts->GetList(i, &account) && account != NULL) { |
| 234 std::string email; |
| 235 if (account->GetString(3, &email) && !email.empty()) |
| 236 account_ids.push_back(email); |
| 237 } |
| 238 } |
| 239 |
| 240 return account_ids; |
| 241 } |
| 242 |
| 243 void AccountReconcilor::OnListAccountsFailure( |
| 244 const GoogleServiceAuthError& error) { |
| 245 gaia_fetcher_.reset(); |
| 246 DVLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString(); |
| 247 |
| 248 are_gaia_accounts_set_ = true; |
| 249 FinishReconcileAction(); |
| 250 } |
| 251 |
| 252 void AccountReconcilor::ValidateAccountsFromTokenService() { |
| 253 primary_account_ = |
| 254 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername(); |
| 255 DCHECK(!primary_account_.empty()); |
| 256 |
| 257 ProfileOAuth2TokenService* token_service = |
| 258 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| 259 chrome_accounts_ = token_service->GetAccounts(); |
| 260 DCHECK(chrome_accounts_.size() > 0); |
| 261 |
| 262 DVLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: " |
| 263 << "Chrome " << chrome_accounts_.size() << " accounts, " |
| 264 << "Primary is '" << primary_account_ << "'"; |
| 265 |
| 266 DCHECK(!requests_); |
| 267 requests_ = |
| 268 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()]; |
| 269 for (size_t i = 0; i < chrome_accounts_.size(); ++i) { |
| 270 requests_[i] = token_service->StartRequest(chrome_accounts_[i], |
| 271 OAuth2TokenService::ScopeSet(), |
| 272 this); |
| 273 } |
| 274 } |
| 275 |
| 276 void AccountReconcilor::OnGetTokenSuccess( |
| 277 const OAuth2TokenService::Request* request, |
| 278 const std::string& access_token, |
| 279 const base::Time& expiration_time) { |
| 280 DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " |
| 281 << request->GetAccountId(); |
| 282 valid_chrome_accounts_.insert(request->GetAccountId()); |
| 283 FinishReconcileAction(); |
| 284 } |
| 285 |
| 286 void AccountReconcilor::OnGetTokenFailure( |
| 287 const OAuth2TokenService::Request* request, |
| 288 const GoogleServiceAuthError& error) { |
| 289 DVLOG(1) << "AccountReconcilor::OnGetTokenSuccess: invalid " |
| 290 << request->GetAccountId(); |
| 291 invalid_chrome_accounts_.insert(request->GetAccountId()); |
| 292 FinishReconcileAction(); |
| 293 } |
| 294 |
| 295 void AccountReconcilor::FinishReconcileAction() { |
| 296 // Make sure that the process of validating the gaia cookie and the oauth2 |
| 297 // tokens individually is done before proceeding with reconciliation. |
| 298 if (!are_gaia_accounts_set_ || |
| 299 (chrome_accounts_.size() != (valid_chrome_accounts_.size() + |
| 300 invalid_chrome_accounts_.size()))) { |
| 301 return; |
| 302 } |
| 303 |
| 304 DVLOG(1) << "AccountReconcilor::FinishReconcileAction"; |
| 305 |
| 306 bool are_primaries_equal = |
| 307 gaia_accounts_.size() > 0 && primary_account_ == gaia_accounts_[0]; |
| 308 bool have_same_accounts = chrome_accounts_.size() == gaia_accounts_.size(); |
| 309 if (have_same_accounts) { |
| 310 for (size_t i = 0; i < gaia_accounts_.size(); ++i) { |
| 311 if (std::find(chrome_accounts_.begin(), chrome_accounts_.end(), |
| 312 gaia_accounts_[i]) == chrome_accounts_.end()) { |
| 313 have_same_accounts = false; |
| 314 break; |
| 315 } |
| 316 } |
| 317 } |
| 318 |
| 319 if (!are_primaries_equal || !have_same_accounts) { |
| 320 // TODO(rogerta): fix things up. |
| 321 } |
| 322 } |
OLD | NEW |