Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/signin/core/browser/account_tracker_service.h" | |
| 6 | |
| 7 #include "base/debug/trace_event.h" | |
| 8 #include "base/prefs/pref_service.h" | |
| 9 #include "base/prefs/scoped_user_pref_update.h" | |
| 10 #include "base/strings/utf_string_conversions.h" | |
| 11 #include "components/signin/core/browser/signin_manager.h" | |
| 12 #include "google_apis/gaia/gaia_auth_util.h" | |
| 13 #include "google_apis/gaia/gaia_constants.h" | |
| 14 #include "google_apis/gaia/gaia_oauth_client.h" | |
| 15 #include "google_apis/gaia/oauth2_token_service.h" | |
| 16 #include "net/url_request/url_request_context_getter.h" | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 const char kAccountKeyPath[] = "account_id"; | |
| 21 const char kAccountEmailPath[] = "email"; | |
| 22 const char kAccountGaiaPath[] = "gaia"; | |
| 23 | |
| 24 } | |
| 25 | |
| 26 class AccountInfoFetcher : public OAuth2TokenService::Consumer, | |
| 27 public gaia::GaiaOAuthClient::Delegate { | |
| 28 public: | |
| 29 AccountInfoFetcher(OAuth2TokenService* token_service, | |
| 30 net::URLRequestContextGetter* request_context_getter, | |
| 31 AccountTrackerService* service, | |
| 32 const std::string& account_id); | |
| 33 virtual ~AccountInfoFetcher(); | |
| 34 | |
| 35 const std::string& account_id() { return account_id_; } | |
| 36 | |
| 37 void Start(); | |
| 38 | |
| 39 // OAuth2TokenService::Consumer implementation. | |
| 40 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, | |
| 41 const std::string& access_token, | |
| 42 const base::Time& expiration_time) OVERRIDE; | |
| 43 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, | |
| 44 const GoogleServiceAuthError& error) OVERRIDE; | |
| 45 | |
| 46 // gaia::GaiaOAuthClient::Delegate implementation. | |
| 47 virtual void OnGetUserInfoResponse( | |
| 48 scoped_ptr<base::DictionaryValue> user_info) OVERRIDE; | |
| 49 virtual void OnOAuthError() OVERRIDE; | |
| 50 virtual void OnNetworkError(int response_code) OVERRIDE; | |
| 51 | |
| 52 private: | |
| 53 OAuth2TokenService* token_service_; | |
| 54 net::URLRequestContextGetter* request_context_getter_; | |
| 55 AccountTrackerService* service_; | |
| 56 const std::string account_id_; | |
| 57 | |
| 58 scoped_ptr<OAuth2TokenService::Request> login_token_request_; | |
| 59 scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; | |
| 60 }; | |
| 61 | |
| 62 AccountInfoFetcher::AccountInfoFetcher( | |
| 63 OAuth2TokenService* token_service, | |
| 64 net::URLRequestContextGetter* request_context_getter, | |
| 65 AccountTrackerService* service, | |
| 66 const std::string& account_id) | |
| 67 : OAuth2TokenService::Consumer("gaia_account_tracker"), | |
| 68 token_service_(token_service), | |
| 69 request_context_getter_(request_context_getter), | |
| 70 service_(service), | |
| 71 account_id_(account_id) { | |
| 72 TRACE_EVENT_ASYNC_BEGIN1( | |
| 73 "AccountTrackerService", "AccountIdFetcher", this, | |
| 74 "account_id", account_id); | |
| 75 } | |
| 76 | |
| 77 AccountInfoFetcher::~AccountInfoFetcher() { | |
| 78 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this); | |
| 79 } | |
| 80 | |
| 81 void AccountInfoFetcher::Start() { | |
| 82 OAuth2TokenService::ScopeSet scopes; | |
| 83 scopes.insert(GaiaConstants::kGoogleUserInfoEmail); | |
| 84 scopes.insert(GaiaConstants::kGoogleUserInfoProfile); | |
| 85 login_token_request_ = token_service_->StartRequest( | |
| 86 account_id_, scopes, this); | |
| 87 } | |
| 88 | |
| 89 void AccountInfoFetcher::OnGetTokenSuccess( | |
| 90 const OAuth2TokenService::Request* request, | |
| 91 const std::string& access_token, | |
| 92 const base::Time& expiration_time) { | |
| 93 TRACE_EVENT_ASYNC_STEP_PAST0( | |
| 94 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess"); | |
| 95 DCHECK_EQ(request, login_token_request_.get()); | |
| 96 | |
| 97 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_)); | |
| 98 | |
| 99 const int kMaxRetries = 3; | |
| 100 gaia_oauth_client_->GetUserInfo(access_token, kMaxRetries, this); | |
| 101 } | |
| 102 | |
| 103 void AccountInfoFetcher::OnGetTokenFailure( | |
| 104 const OAuth2TokenService::Request* request, | |
| 105 const GoogleServiceAuthError& error) { | |
| 106 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService", | |
| 107 "AccountIdFetcher", | |
| 108 this, | |
| 109 "OnGetTokenFailure", | |
| 110 "google_service_auth_error", | |
| 111 error.ToString()); | |
| 112 LOG(ERROR) << "OnGetTokenFailure: " << error.ToString(); | |
| 113 DCHECK_EQ(request, login_token_request_.get()); | |
| 114 service_->OnUserInfoFetchFailure(this); | |
| 115 } | |
| 116 | |
| 117 void AccountInfoFetcher::OnGetUserInfoResponse( | |
| 118 scoped_ptr<base::DictionaryValue> user_info) { | |
| 119 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService", | |
| 120 "AccountIdFetcher", | |
| 121 this, | |
| 122 "OnGetUserInfoResponse", | |
| 123 "account_id", | |
| 124 account_id_); | |
| 125 service_->OnUserInfoFetchSuccess(this, user_info.get()); | |
| 126 } | |
| 127 | |
| 128 void AccountInfoFetcher::OnOAuthError() { | |
| 129 TRACE_EVENT_ASYNC_STEP_PAST0( | |
| 130 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError"); | |
| 131 LOG(ERROR) << "OnOAuthError"; | |
| 132 service_->OnUserInfoFetchFailure(this); | |
| 133 } | |
| 134 | |
| 135 void AccountInfoFetcher::OnNetworkError(int response_code) { | |
| 136 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService", | |
| 137 "AccountIdFetcher", | |
| 138 this, | |
| 139 "OnNetworkError", | |
| 140 "response_code", | |
| 141 response_code); | |
| 142 LOG(ERROR) << "OnNetworkError " << response_code; | |
| 143 service_->OnUserInfoFetchFailure(this); | |
| 144 } | |
| 145 | |
| 146 | |
| 147 const char AccountTrackerService::kAccountInfoPref[] = "account_info"; | |
| 148 | |
| 149 AccountTrackerService::AccountTrackerService() | |
| 150 : token_service_(NULL), | |
| 151 pref_service_(NULL), | |
| 152 shutdown_called_(false) { | |
| 153 } | |
| 154 | |
| 155 AccountTrackerService::~AccountTrackerService() { | |
| 156 DCHECK(shutdown_called_); | |
| 157 } | |
| 158 | |
| 159 void AccountTrackerService::Initialize( | |
| 160 OAuth2TokenService* token_service, | |
| 161 PrefService* pref_service, | |
| 162 net::URLRequestContextGetter* request_context_getter) { | |
| 163 DCHECK(token_service); | |
| 164 DCHECK(!token_service_); | |
| 165 DCHECK(pref_service); | |
| 166 DCHECK(!pref_service_); | |
| 167 token_service_ = token_service; | |
| 168 pref_service_ = pref_service; | |
| 169 request_context_getter_ = request_context_getter; | |
| 170 token_service_->AddObserver(this); | |
| 171 LoadFromPrefs(); | |
| 172 LoadFromTokenService(); | |
| 173 } | |
| 174 | |
| 175 void AccountTrackerService::Shutdown() { | |
| 176 shutdown_called_ = true; | |
| 177 STLDeleteValues(&user_info_requests_); | |
| 178 token_service_->RemoveObserver(this); | |
| 179 } | |
| 180 | |
| 181 void AccountTrackerService::AddObserver(Observer* observer) { | |
| 182 observer_list_.AddObserver(observer); | |
| 183 } | |
| 184 | |
| 185 void AccountTrackerService::RemoveObserver(Observer* observer) { | |
| 186 observer_list_.RemoveObserver(observer); | |
| 187 } | |
| 188 | |
| 189 bool AccountTrackerService::IsAllUserInfoFetched() const { | |
| 190 return user_info_requests_.empty(); | |
| 191 } | |
| 192 | |
| 193 std::vector<AccountTrackerService::AccountInfo> | |
| 194 AccountTrackerService::GetAccounts() const { | |
| 195 std::vector<AccountInfo> accounts; | |
| 196 | |
| 197 for (std::map<std::string, AccountState>::const_iterator it = | |
| 198 accounts_.begin(); | |
| 199 it != accounts_.end(); | |
| 200 ++it) { | |
| 201 const AccountState& state = it->second; | |
| 202 accounts.push_back(state.info); | |
| 203 } | |
| 204 return accounts; | |
| 205 } | |
| 206 | |
| 207 AccountTrackerService::AccountInfo AccountTrackerService::GetAccountInfo( | |
| 208 const std::string& account_id) { | |
| 209 if (ContainsKey(accounts_, account_id)) | |
| 210 return accounts_[account_id].info; | |
| 211 | |
| 212 return AccountInfo(); | |
| 213 } | |
| 214 | |
| 215 AccountTrackerService::AccountInfo | |
| 216 AccountTrackerService::FindAccountInfoByGaiaId( | |
| 217 const std::string& gaia_id) { | |
| 218 for (std::map<std::string, AccountState>::const_iterator it = | |
| 219 accounts_.begin(); | |
| 220 it != accounts_.end(); | |
| 221 ++it) { | |
| 222 const AccountState& state = it->second; | |
| 223 if (state.info.gaia == gaia_id) { | |
| 224 return state.info; | |
|
guohui
2014/08/20 18:05:23
nits: no need for {}
Roger Tawa OOO till Jul 10th
2014/08/21 01:30:17
Done.
| |
| 225 } | |
| 226 } | |
| 227 | |
| 228 return AccountInfo(); | |
| 229 } | |
| 230 | |
| 231 AccountTrackerService::AccountInfo | |
| 232 AccountTrackerService::FindAccountInfoByEmail( | |
| 233 const std::string& email) { | |
| 234 for (std::map<std::string, AccountState>::const_iterator it = | |
| 235 accounts_.begin(); | |
| 236 it != accounts_.end(); | |
| 237 ++it) { | |
| 238 const AccountState& state = it->second; | |
| 239 if (gaia::AreEmailsSame(state.info.email, email)) { | |
| 240 return state.info; | |
|
guohui
2014/08/20 18:05:23
nits: no need for {}
Roger Tawa OOO till Jul 10th
2014/08/21 01:30:16
Done.
| |
| 241 } | |
| 242 } | |
| 243 | |
| 244 return AccountInfo(); | |
| 245 } | |
| 246 | |
| 247 void AccountTrackerService::OnRefreshTokenAvailable( | |
| 248 const std::string& account_id) { | |
| 249 TRACE_EVENT1("AccountTrackerService", | |
| 250 "AccountTracker::OnRefreshTokenAvailable", | |
| 251 "account_id", | |
| 252 account_id); | |
| 253 DVLOG(1) << "AVAILABLE " << account_id; | |
| 254 | |
| 255 StartTrackingAccount(account_id); | |
| 256 AccountState& state = accounts_[account_id]; | |
| 257 | |
| 258 if (state.info.gaia.empty()) { | |
| 259 StartFetchingUserInfo(account_id); | |
| 260 } else { | |
| 261 NotifyAccountUpdated(state); | |
|
guohui
2014/08/20 18:05:23
why do we notify account updated here? It seems he
Roger Tawa OOO till Jul 10th
2014/08/21 01:30:16
Oops, correct. That's a left over from when the c
| |
| 262 } | |
| 263 } | |
| 264 | |
| 265 void AccountTrackerService::OnRefreshTokenRevoked( | |
| 266 const std::string& account_id) { | |
| 267 TRACE_EVENT1("AccountTrackerService", | |
| 268 "AccountTracker::OnRefreshTokenRevoked", | |
| 269 "account_id", | |
| 270 account_id); | |
| 271 | |
| 272 DVLOG(1) << "REVOKED " << account_id; | |
| 273 StopTrackingAccount(account_id); | |
| 274 } | |
| 275 | |
| 276 void AccountTrackerService::NotifyAccountUpdated(const AccountState& state) { | |
| 277 DCHECK(!state.info.gaia.empty()); | |
| 278 FOR_EACH_OBSERVER( | |
| 279 Observer, observer_list_, OnAccountUpdated(state.info)); | |
| 280 } | |
| 281 | |
| 282 void AccountTrackerService::NotifyAccountRemoved(const AccountState& state) { | |
| 283 DCHECK(!state.info.gaia.empty()); | |
| 284 FOR_EACH_OBSERVER( | |
| 285 Observer, observer_list_, OnAccountRemoved(state.info)); | |
|
guohui
2014/08/20 18:05:23
what is the purpose of OnAccountRemoved? It is fir
Roger Tawa OOO till Jul 10th
2014/08/21 01:30:16
Good point. Should not remove if fetcher fails.
| |
| 286 } | |
| 287 | |
| 288 void AccountTrackerService::StartTrackingAccount( | |
| 289 const std::string& account_id) { | |
| 290 if (!ContainsKey(accounts_, account_id)) { | |
| 291 DVLOG(1) << "StartTracking " << account_id; | |
| 292 AccountState state; | |
| 293 state.info.account_id = account_id; | |
| 294 accounts_.insert(make_pair(account_id, state)); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void AccountTrackerService::StopTrackingAccount(const std::string& account_id) { | |
| 299 DVLOG(1) << "StopTracking " << account_id; | |
| 300 if (ContainsKey(accounts_, account_id)) { | |
| 301 AccountState& state = accounts_[account_id]; | |
| 302 RemoveFromPrefs(state); | |
| 303 if (!state.info.gaia.empty()) | |
| 304 NotifyAccountRemoved(state); | |
| 305 | |
| 306 accounts_.erase(account_id); | |
| 307 } | |
| 308 | |
| 309 if (ContainsKey(user_info_requests_, account_id)) | |
| 310 DeleteFetcher(user_info_requests_[account_id]); | |
| 311 } | |
| 312 | |
| 313 void AccountTrackerService::StartFetchingUserInfo( | |
| 314 const std::string& account_id) { | |
| 315 if (ContainsKey(user_info_requests_, account_id)) | |
| 316 DeleteFetcher(user_info_requests_[account_id]); | |
| 317 | |
| 318 DVLOG(1) << "StartFetching " << account_id; | |
| 319 AccountInfoFetcher* fetcher = | |
| 320 new AccountInfoFetcher(token_service_, | |
| 321 request_context_getter_.get(), | |
| 322 this, | |
| 323 account_id); | |
| 324 user_info_requests_[account_id] = fetcher; | |
| 325 fetcher->Start(); | |
| 326 } | |
| 327 | |
| 328 void AccountTrackerService::OnUserInfoFetchSuccess( | |
| 329 AccountInfoFetcher* fetcher, | |
| 330 const base::DictionaryValue* user_info) { | |
| 331 const std::string& account_id = fetcher->account_id(); | |
| 332 DCHECK(ContainsKey(accounts_, account_id)); | |
| 333 AccountState& state = accounts_[account_id]; | |
| 334 | |
| 335 std::string gaia_id; | |
| 336 std::string email; | |
| 337 if (user_info->GetString("id", &gaia_id) && | |
| 338 user_info->GetString("email", &email)) { | |
| 339 state.info.gaia = gaia_id; | |
| 340 state.info.email = email; | |
| 341 | |
| 342 NotifyAccountUpdated(state); | |
| 343 SaveToPrefs(state); | |
| 344 } | |
| 345 DeleteFetcher(fetcher); | |
| 346 } | |
| 347 | |
| 348 void AccountTrackerService::OnUserInfoFetchFailure( | |
| 349 AccountInfoFetcher* fetcher) { | |
| 350 LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_id(); | |
| 351 std::string account_id = fetcher->account_id(); | |
| 352 DeleteFetcher(fetcher); | |
| 353 StopTrackingAccount(account_id); | |
| 354 } | |
| 355 | |
| 356 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher* fetcher) { | |
| 357 DVLOG(1) << "DeleteFetcher " << fetcher->account_id(); | |
| 358 const std::string& account_id = fetcher->account_id(); | |
| 359 DCHECK(ContainsKey(user_info_requests_, account_id)); | |
| 360 DCHECK_EQ(fetcher, user_info_requests_[account_id]); | |
| 361 user_info_requests_.erase(account_id); | |
| 362 delete fetcher; | |
| 363 } | |
| 364 | |
| 365 void AccountTrackerService::LoadFromPrefs() { | |
| 366 const base::ListValue* list = pref_service_->GetList(kAccountInfoPref); | |
| 367 for (size_t i = 0; i < list->GetSize(); ++i) { | |
| 368 const base::DictionaryValue* dict; | |
| 369 if (list->GetDictionary(i, &dict)) { | |
| 370 base::string16 value; | |
| 371 if (dict->GetString(kAccountKeyPath, &value)) { | |
| 372 std::string account_id = base::UTF16ToUTF8(value); | |
| 373 StartTrackingAccount(account_id); | |
| 374 AccountState& state = accounts_[account_id]; | |
| 375 | |
| 376 if (dict->GetString(kAccountGaiaPath, &value)) | |
| 377 state.info.gaia = base::UTF16ToUTF8(value); | |
| 378 if (dict->GetString(kAccountEmailPath, &value)) | |
| 379 state.info.email = base::UTF16ToUTF8(value); | |
|
guohui
2014/08/20 18:05:23
should we notify account updated here?
Roger Tawa OOO till Jul 10th
2014/08/21 01:30:16
Yes, but only if gaiaid is not empty.
| |
| 380 } | |
| 381 } | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 void AccountTrackerService::SaveToPrefs(const AccountState& state) { | |
| 386 if (!pref_service_) | |
| 387 return; | |
| 388 | |
| 389 base::DictionaryValue* dict = NULL; | |
| 390 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id); | |
| 391 ListPrefUpdate update(pref_service_, kAccountInfoPref); | |
| 392 for(size_t i = 0; i < update->GetSize(); ++i, dict = NULL) { | |
| 393 if (update->GetDictionary(i, &dict)) { | |
| 394 base::string16 value; | |
| 395 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) | |
| 396 break; | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 if (!dict) { | |
| 401 dict = new base::DictionaryValue(); | |
| 402 update->Append(dict); // |update| takes ownership. | |
| 403 dict->SetString(kAccountKeyPath, account_id_16); | |
| 404 } | |
| 405 | |
| 406 dict->SetString(kAccountEmailPath, state.info.email); | |
| 407 dict->SetString(kAccountGaiaPath, state.info.gaia); | |
| 408 } | |
| 409 | |
| 410 void AccountTrackerService::RemoveFromPrefs(const AccountState& state) { | |
| 411 if (!pref_service_) | |
| 412 return; | |
| 413 | |
| 414 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id); | |
| 415 ListPrefUpdate update(pref_service_, kAccountInfoPref); | |
| 416 for(size_t i = 0; i < update->GetSize(); ++i) { | |
| 417 base::DictionaryValue* dict = NULL; | |
| 418 if (update->GetDictionary(i, &dict)) { | |
| 419 base::string16 value; | |
| 420 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) { | |
| 421 update->Remove(i, NULL); | |
| 422 break; | |
| 423 } | |
| 424 } | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void AccountTrackerService::LoadFromTokenService() { | |
| 429 std::vector<std::string> accounts = token_service_->GetAccounts(); | |
| 430 for (std::vector<std::string>::const_iterator it = accounts.begin(); | |
| 431 it != accounts.end(); ++it) { | |
| 432 OnRefreshTokenAvailable(*it); | |
| 433 } | |
| 434 } | |
| OLD | NEW |