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; |
| 225 } |
| 226 |
| 227 return AccountInfo(); |
| 228 } |
| 229 |
| 230 AccountTrackerService::AccountInfo |
| 231 AccountTrackerService::FindAccountInfoByEmail( |
| 232 const std::string& email) { |
| 233 for (std::map<std::string, AccountState>::const_iterator it = |
| 234 accounts_.begin(); |
| 235 it != accounts_.end(); |
| 236 ++it) { |
| 237 const AccountState& state = it->second; |
| 238 if (gaia::AreEmailsSame(state.info.email, email)) |
| 239 return state.info; |
| 240 } |
| 241 |
| 242 return AccountInfo(); |
| 243 } |
| 244 |
| 245 void AccountTrackerService::OnRefreshTokenAvailable( |
| 246 const std::string& account_id) { |
| 247 TRACE_EVENT1("AccountTrackerService", |
| 248 "AccountTracker::OnRefreshTokenAvailable", |
| 249 "account_id", |
| 250 account_id); |
| 251 DVLOG(1) << "AVAILABLE " << account_id; |
| 252 |
| 253 StartTrackingAccount(account_id); |
| 254 AccountState& state = accounts_[account_id]; |
| 255 |
| 256 if (state.info.gaia.empty()) |
| 257 StartFetchingUserInfo(account_id); |
| 258 } |
| 259 |
| 260 void AccountTrackerService::OnRefreshTokenRevoked( |
| 261 const std::string& account_id) { |
| 262 TRACE_EVENT1("AccountTrackerService", |
| 263 "AccountTracker::OnRefreshTokenRevoked", |
| 264 "account_id", |
| 265 account_id); |
| 266 |
| 267 DVLOG(1) << "REVOKED " << account_id; |
| 268 StopTrackingAccount(account_id); |
| 269 } |
| 270 |
| 271 void AccountTrackerService::NotifyAccountUpdated(const AccountState& state) { |
| 272 DCHECK(!state.info.gaia.empty()); |
| 273 FOR_EACH_OBSERVER( |
| 274 Observer, observer_list_, OnAccountUpdated(state.info)); |
| 275 } |
| 276 |
| 277 void AccountTrackerService::NotifyAccountRemoved(const AccountState& state) { |
| 278 DCHECK(!state.info.gaia.empty()); |
| 279 FOR_EACH_OBSERVER( |
| 280 Observer, observer_list_, OnAccountRemoved(state.info)); |
| 281 } |
| 282 |
| 283 void AccountTrackerService::StartTrackingAccount( |
| 284 const std::string& account_id) { |
| 285 if (!ContainsKey(accounts_, account_id)) { |
| 286 DVLOG(1) << "StartTracking " << account_id; |
| 287 AccountState state; |
| 288 state.info.account_id = account_id; |
| 289 accounts_.insert(make_pair(account_id, state)); |
| 290 } |
| 291 } |
| 292 |
| 293 void AccountTrackerService::StopTrackingAccount(const std::string& account_id) { |
| 294 DVLOG(1) << "StopTracking " << account_id; |
| 295 if (ContainsKey(accounts_, account_id)) { |
| 296 AccountState& state = accounts_[account_id]; |
| 297 RemoveFromPrefs(state); |
| 298 if (!state.info.gaia.empty()) |
| 299 NotifyAccountRemoved(state); |
| 300 |
| 301 accounts_.erase(account_id); |
| 302 } |
| 303 |
| 304 if (ContainsKey(user_info_requests_, account_id)) |
| 305 DeleteFetcher(user_info_requests_[account_id]); |
| 306 } |
| 307 |
| 308 void AccountTrackerService::StartFetchingUserInfo( |
| 309 const std::string& account_id) { |
| 310 if (ContainsKey(user_info_requests_, account_id)) |
| 311 DeleteFetcher(user_info_requests_[account_id]); |
| 312 |
| 313 DVLOG(1) << "StartFetching " << account_id; |
| 314 AccountInfoFetcher* fetcher = |
| 315 new AccountInfoFetcher(token_service_, |
| 316 request_context_getter_.get(), |
| 317 this, |
| 318 account_id); |
| 319 user_info_requests_[account_id] = fetcher; |
| 320 fetcher->Start(); |
| 321 } |
| 322 |
| 323 void AccountTrackerService::OnUserInfoFetchSuccess( |
| 324 AccountInfoFetcher* fetcher, |
| 325 const base::DictionaryValue* user_info) { |
| 326 const std::string& account_id = fetcher->account_id(); |
| 327 DCHECK(ContainsKey(accounts_, account_id)); |
| 328 AccountState& state = accounts_[account_id]; |
| 329 |
| 330 std::string gaia_id; |
| 331 std::string email; |
| 332 if (user_info->GetString("id", &gaia_id) && |
| 333 user_info->GetString("email", &email)) { |
| 334 state.info.gaia = gaia_id; |
| 335 state.info.email = email; |
| 336 |
| 337 NotifyAccountUpdated(state); |
| 338 SaveToPrefs(state); |
| 339 } |
| 340 DeleteFetcher(fetcher); |
| 341 } |
| 342 |
| 343 void AccountTrackerService::OnUserInfoFetchFailure( |
| 344 AccountInfoFetcher* fetcher) { |
| 345 LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_id(); |
| 346 DeleteFetcher(fetcher); |
| 347 // TODO(rogerta): figure out when to retry. |
| 348 } |
| 349 |
| 350 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher* fetcher) { |
| 351 DVLOG(1) << "DeleteFetcher " << fetcher->account_id(); |
| 352 const std::string& account_id = fetcher->account_id(); |
| 353 DCHECK(ContainsKey(user_info_requests_, account_id)); |
| 354 DCHECK_EQ(fetcher, user_info_requests_[account_id]); |
| 355 user_info_requests_.erase(account_id); |
| 356 delete fetcher; |
| 357 } |
| 358 |
| 359 void AccountTrackerService::LoadFromPrefs() { |
| 360 const base::ListValue* list = pref_service_->GetList(kAccountInfoPref); |
| 361 for (size_t i = 0; i < list->GetSize(); ++i) { |
| 362 const base::DictionaryValue* dict; |
| 363 if (list->GetDictionary(i, &dict)) { |
| 364 base::string16 value; |
| 365 if (dict->GetString(kAccountKeyPath, &value)) { |
| 366 std::string account_id = base::UTF16ToUTF8(value); |
| 367 StartTrackingAccount(account_id); |
| 368 AccountState& state = accounts_[account_id]; |
| 369 |
| 370 if (dict->GetString(kAccountGaiaPath, &value)) |
| 371 state.info.gaia = base::UTF16ToUTF8(value); |
| 372 if (dict->GetString(kAccountEmailPath, &value)) |
| 373 state.info.email = base::UTF16ToUTF8(value); |
| 374 |
| 375 if (!state.info.gaia.empty()) |
| 376 NotifyAccountUpdated(state); |
| 377 } |
| 378 } |
| 379 } |
| 380 } |
| 381 |
| 382 void AccountTrackerService::SaveToPrefs(const AccountState& state) { |
| 383 if (!pref_service_) |
| 384 return; |
| 385 |
| 386 base::DictionaryValue* dict = NULL; |
| 387 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id); |
| 388 ListPrefUpdate update(pref_service_, kAccountInfoPref); |
| 389 for(size_t i = 0; i < update->GetSize(); ++i, dict = NULL) { |
| 390 if (update->GetDictionary(i, &dict)) { |
| 391 base::string16 value; |
| 392 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) |
| 393 break; |
| 394 } |
| 395 } |
| 396 |
| 397 if (!dict) { |
| 398 dict = new base::DictionaryValue(); |
| 399 update->Append(dict); // |update| takes ownership. |
| 400 dict->SetString(kAccountKeyPath, account_id_16); |
| 401 } |
| 402 |
| 403 dict->SetString(kAccountEmailPath, state.info.email); |
| 404 dict->SetString(kAccountGaiaPath, state.info.gaia); |
| 405 } |
| 406 |
| 407 void AccountTrackerService::RemoveFromPrefs(const AccountState& state) { |
| 408 if (!pref_service_) |
| 409 return; |
| 410 |
| 411 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id); |
| 412 ListPrefUpdate update(pref_service_, kAccountInfoPref); |
| 413 for(size_t i = 0; i < update->GetSize(); ++i) { |
| 414 base::DictionaryValue* dict = NULL; |
| 415 if (update->GetDictionary(i, &dict)) { |
| 416 base::string16 value; |
| 417 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) { |
| 418 update->Remove(i, NULL); |
| 419 break; |
| 420 } |
| 421 } |
| 422 } |
| 423 } |
| 424 |
| 425 void AccountTrackerService::LoadFromTokenService() { |
| 426 std::vector<std::string> accounts = token_service_->GetAccounts(); |
| 427 for (std::vector<std::string>::const_iterator it = accounts.begin(); |
| 428 it != accounts.end(); ++it) { |
| 429 OnRefreshTokenAvailable(*it); |
| 430 } |
| 431 } |
OLD | NEW |