Add abhijeet.k@samsung.com to AUTHORS list.
[chromium-blink-merge.git] / components / signin / core / browser / account_tracker_service.cc
blob2da5a98656f6a141fe48a960822442321be68d20
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.
5 #include "components/signin/core/browser/account_tracker_service.h"
7 #include "base/callback.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/trace_event/trace_event.h"
15 #include "components/signin/core/browser/refresh_token_annotation_request.h"
16 #include "components/signin/core/browser/signin_client.h"
17 #include "components/signin/core/browser/signin_manager.h"
18 #include "components/signin/core/common/signin_pref_names.h"
19 #include "components/signin/core/common/signin_switches.h"
20 #include "google_apis/gaia/gaia_auth_util.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 #include "google_apis/gaia/gaia_oauth_client.h"
23 #include "net/url_request/url_request_context_getter.h"
25 namespace {
27 const char kAccountKeyPath[] = "account_id";
28 const char kAccountEmailPath[] = "email";
29 const char kAccountGaiaPath[] = "gaia";
30 const char kAccountHostedDomainPath[] = "hd";
32 #if !defined(OS_ANDROID) && !defined(OS_IOS)
33 // IsRefreshTokenDeviceIdExperimentEnabled is called from
34 // SendRefreshTokenAnnotationRequest only on desktop platforms.
35 bool IsRefreshTokenDeviceIdExperimentEnabled() {
36 const std::string group_name =
37 base::FieldTrialList::FindFullName("RefreshTokenDeviceId");
38 return group_name == "Enabled";
40 #endif
43 // This must be a string which can never be a valid domain.
44 const char AccountTrackerService::kNoHostedDomainFound[] = "NO_HOSTED_DOMAIN";
46 class AccountInfoFetcher : public OAuth2TokenService::Consumer,
47 public gaia::GaiaOAuthClient::Delegate {
48 public:
49 AccountInfoFetcher(OAuth2TokenService* token_service,
50 net::URLRequestContextGetter* request_context_getter,
51 AccountTrackerService* service,
52 const std::string& account_id);
53 ~AccountInfoFetcher() override;
55 const std::string& account_id() { return account_id_; }
57 void Start();
59 // OAuth2TokenService::Consumer implementation.
60 void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
61 const std::string& access_token,
62 const base::Time& expiration_time) override;
63 void OnGetTokenFailure(const OAuth2TokenService::Request* request,
64 const GoogleServiceAuthError& error) override;
66 // gaia::GaiaOAuthClient::Delegate implementation.
67 void OnGetUserInfoResponse(
68 scoped_ptr<base::DictionaryValue> user_info) override;
69 void OnOAuthError() override;
70 void OnNetworkError(int response_code) override;
72 private:
73 OAuth2TokenService* token_service_;
74 net::URLRequestContextGetter* request_context_getter_;
75 AccountTrackerService* service_;
76 const std::string account_id_;
78 scoped_ptr<OAuth2TokenService::Request> login_token_request_;
79 scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
82 AccountInfoFetcher::AccountInfoFetcher(
83 OAuth2TokenService* token_service,
84 net::URLRequestContextGetter* request_context_getter,
85 AccountTrackerService* service,
86 const std::string& account_id)
87 : OAuth2TokenService::Consumer("gaia_account_tracker"),
88 token_service_(token_service),
89 request_context_getter_(request_context_getter),
90 service_(service),
91 account_id_(account_id) {
92 TRACE_EVENT_ASYNC_BEGIN1(
93 "AccountTrackerService", "AccountIdFetcher", this,
94 "account_id", account_id);
97 AccountInfoFetcher::~AccountInfoFetcher() {
98 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this);
101 void AccountInfoFetcher::Start() {
102 OAuth2TokenService::ScopeSet scopes;
103 scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
104 scopes.insert(GaiaConstants::kGoogleUserInfoProfile);
105 login_token_request_ = token_service_->StartRequest(
106 account_id_, scopes, this);
109 void AccountInfoFetcher::OnGetTokenSuccess(
110 const OAuth2TokenService::Request* request,
111 const std::string& access_token,
112 const base::Time& expiration_time) {
113 TRACE_EVENT_ASYNC_STEP_PAST0(
114 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess");
115 DCHECK_EQ(request, login_token_request_.get());
117 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_));
119 const int kMaxRetries = 3;
120 gaia_oauth_client_->GetUserInfo(access_token, kMaxRetries, this);
123 void AccountInfoFetcher::OnGetTokenFailure(
124 const OAuth2TokenService::Request* request,
125 const GoogleServiceAuthError& error) {
126 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
127 "AccountIdFetcher",
128 this,
129 "OnGetTokenFailure",
130 "google_service_auth_error",
131 error.ToString());
132 LOG(ERROR) << "OnGetTokenFailure: " << error.ToString();
133 DCHECK_EQ(request, login_token_request_.get());
134 service_->OnUserInfoFetchFailure(this);
137 void AccountInfoFetcher::OnGetUserInfoResponse(
138 scoped_ptr<base::DictionaryValue> user_info) {
139 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
140 "AccountIdFetcher",
141 this,
142 "OnGetUserInfoResponse",
143 "account_id",
144 account_id_);
145 service_->OnUserInfoFetchSuccess(this, user_info.get());
148 void AccountInfoFetcher::OnOAuthError() {
149 TRACE_EVENT_ASYNC_STEP_PAST0(
150 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError");
151 LOG(ERROR) << "OnOAuthError";
152 service_->OnUserInfoFetchFailure(this);
155 void AccountInfoFetcher::OnNetworkError(int response_code) {
156 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
157 "AccountIdFetcher",
158 this,
159 "OnNetworkError",
160 "response_code",
161 response_code);
162 LOG(ERROR) << "OnNetworkError " << response_code;
163 service_->OnUserInfoFetchFailure(this);
166 AccountTrackerService::AccountInfo::AccountInfo() {}
167 AccountTrackerService::AccountInfo::~AccountInfo() {}
169 bool AccountTrackerService::AccountInfo::IsValid() {
170 return account_id.empty() || email.empty() || gaia.empty() ||
171 hosted_domain.empty();
175 const char AccountTrackerService::kAccountInfoPref[] = "account_info";
177 AccountTrackerService::AccountTrackerService()
178 : token_service_(NULL),
179 signin_client_(NULL),
180 shutdown_called_(false),
181 network_fetches_enabled_(false) {
184 AccountTrackerService::~AccountTrackerService() {
185 DCHECK(shutdown_called_);
188 void AccountTrackerService::Initialize(
189 OAuth2TokenService* token_service,
190 SigninClient* signin_client) {
191 DCHECK(token_service);
192 DCHECK(!token_service_);
193 DCHECK(signin_client);
194 DCHECK(!signin_client_);
195 token_service_ = token_service;
196 signin_client_ = signin_client;
197 token_service_->AddObserver(this);
198 LoadFromPrefs();
199 LoadFromTokenService();
202 void AccountTrackerService::EnableNetworkFetches() {
203 DCHECK(CalledOnValidThread());
204 DCHECK(!network_fetches_enabled_);
205 network_fetches_enabled_ = true;
206 for (std::string account_id : pending_user_info_fetches_)
207 StartFetchingUserInfo(account_id);
208 pending_user_info_fetches_.clear();
211 void AccountTrackerService::Shutdown() {
212 shutdown_called_ = true;
213 STLDeleteValues(&user_info_requests_);
214 token_service_->RemoveObserver(this);
217 void AccountTrackerService::AddObserver(Observer* observer) {
218 observer_list_.AddObserver(observer);
221 void AccountTrackerService::RemoveObserver(Observer* observer) {
222 observer_list_.RemoveObserver(observer);
225 bool AccountTrackerService::IsAllUserInfoFetched() const {
226 return user_info_requests_.empty();
229 std::vector<AccountTrackerService::AccountInfo>
230 AccountTrackerService::GetAccounts() const {
231 std::vector<AccountInfo> accounts;
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 accounts.push_back(state.info);
240 return accounts;
243 AccountTrackerService::AccountInfo AccountTrackerService::GetAccountInfo(
244 const std::string& account_id) {
245 if (ContainsKey(accounts_, account_id))
246 return accounts_[account_id].info;
248 return AccountInfo();
251 AccountTrackerService::AccountInfo
252 AccountTrackerService::FindAccountInfoByGaiaId(
253 const std::string& gaia_id) {
254 for (std::map<std::string, AccountState>::const_iterator it =
255 accounts_.begin();
256 it != accounts_.end();
257 ++it) {
258 const AccountState& state = it->second;
259 if (state.info.gaia == gaia_id)
260 return state.info;
263 return AccountInfo();
266 AccountTrackerService::AccountInfo
267 AccountTrackerService::FindAccountInfoByEmail(
268 const std::string& email) {
269 for (std::map<std::string, AccountState>::const_iterator it =
270 accounts_.begin();
271 it != accounts_.end();
272 ++it) {
273 const AccountState& state = it->second;
274 if (gaia::AreEmailsSame(state.info.email, email))
275 return state.info;
278 return AccountInfo();
281 AccountTrackerService::AccountIdMigrationState
282 AccountTrackerService::GetMigrationState() {
283 return GetMigrationState(signin_client_->GetPrefs());
286 // static
287 AccountTrackerService::AccountIdMigrationState
288 AccountTrackerService::GetMigrationState(PrefService* pref_service) {
289 return static_cast<AccountTrackerService::AccountIdMigrationState>(
290 pref_service->GetInteger(prefs::kAccountIdMigrationState));
293 void AccountTrackerService::OnRefreshTokenAvailable(
294 const std::string& account_id) {
295 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
296 // fixed.
297 tracked_objects::ScopedTracker tracking_profile(
298 FROM_HERE_WITH_EXPLICIT_FUNCTION(
299 "422460 AccountTrackerService::OnRefreshTokenAvailable"));
301 TRACE_EVENT1("AccountTrackerService",
302 "AccountTracker::OnRefreshTokenAvailable",
303 "account_id",
304 account_id);
305 DVLOG(1) << "AVAILABLE " << account_id;
307 StartTrackingAccount(account_id);
308 AccountState& state = accounts_[account_id];
310 // Don't bother fetching for supervised users since this causes the token
311 // service to raise spurious auth errors.
312 // TODO(treib): this string is also used in supervised_user_constants.cc.
313 // Should put in a common place.
314 if (account_id == "managed_user@localhost")
315 return;
317 #if defined(OS_ANDROID)
318 // TODO(mlerman): Change this condition back to state.info.IsValid() and
319 // ensure the Fetch doesn't occur until after ProfileImpl::OnPrefsLoaded().
320 if (state.info.gaia.empty())
321 #else
322 if (state.info.IsValid())
323 #endif
324 StartFetchingUserInfo(account_id);
326 SendRefreshTokenAnnotationRequest(account_id);
329 void AccountTrackerService::OnRefreshTokenRevoked(
330 const std::string& account_id) {
331 TRACE_EVENT1("AccountTrackerService",
332 "AccountTracker::OnRefreshTokenRevoked",
333 "account_id",
334 account_id);
336 DVLOG(1) << "REVOKED " << account_id;
337 StopTrackingAccount(account_id);
340 void AccountTrackerService::NotifyAccountUpdated(const AccountState& state) {
341 DCHECK(!state.info.gaia.empty());
342 FOR_EACH_OBSERVER(
343 Observer, observer_list_, OnAccountUpdated(state.info));
346 void AccountTrackerService::NotifyAccountUpdateFailed(
347 const std::string& account_id) {
348 FOR_EACH_OBSERVER(
349 Observer, observer_list_, OnAccountUpdateFailed(account_id));
352 void AccountTrackerService::NotifyAccountRemoved(const AccountState& state) {
353 DCHECK(!state.info.gaia.empty());
354 FOR_EACH_OBSERVER(
355 Observer, observer_list_, OnAccountRemoved(state.info));
358 void AccountTrackerService::StartTrackingAccount(
359 const std::string& account_id) {
360 if (!ContainsKey(accounts_, account_id)) {
361 DVLOG(1) << "StartTracking " << account_id;
362 AccountState state;
363 state.info.account_id = account_id;
364 accounts_.insert(make_pair(account_id, state));
368 void AccountTrackerService::StopTrackingAccount(const std::string& account_id) {
369 DVLOG(1) << "StopTracking " << account_id;
370 if (ContainsKey(accounts_, account_id)) {
371 AccountState& state = accounts_[account_id];
372 RemoveFromPrefs(state);
373 if (!state.info.gaia.empty())
374 NotifyAccountRemoved(state);
376 accounts_.erase(account_id);
379 if (ContainsKey(user_info_requests_, account_id))
380 DeleteFetcher(user_info_requests_[account_id]);
383 void AccountTrackerService::StartFetchingUserInfo(
384 const std::string& account_id) {
385 DCHECK(CalledOnValidThread());
386 if (!network_fetches_enabled_) {
387 pending_user_info_fetches_.push_back(account_id);
388 return;
391 if (ContainsKey(user_info_requests_, account_id))
392 DeleteFetcher(user_info_requests_[account_id]);
394 DVLOG(1) << "StartFetching " << account_id;
395 AccountInfoFetcher* fetcher =
396 new AccountInfoFetcher(token_service_,
397 signin_client_->GetURLRequestContext(),
398 this,
399 account_id);
400 user_info_requests_[account_id] = fetcher;
401 fetcher->Start();
404 void AccountTrackerService::SetAccountStateFromUserInfo(
405 const std::string& account_id,
406 const base::DictionaryValue* user_info) {
407 AccountState& state = accounts_[account_id];
409 std::string gaia_id;
410 std::string email;
411 if (user_info->GetString("id", &gaia_id) &&
412 user_info->GetString("email", &email)) {
413 state.info.gaia = gaia_id;
414 state.info.email = email;
416 std::string hosted_domain;
417 if (user_info->GetString("hd", &hosted_domain) && !hosted_domain.empty()) {
418 state.info.hosted_domain = hosted_domain;
419 } else {
420 state.info.hosted_domain = kNoHostedDomainFound;
423 NotifyAccountUpdated(state);
424 SaveToPrefs(state);
428 void AccountTrackerService::OnUserInfoFetchSuccess(
429 AccountInfoFetcher* fetcher,
430 const base::DictionaryValue* user_info) {
431 const std::string& account_id = fetcher->account_id();
432 DCHECK(ContainsKey(accounts_, account_id));
434 SetAccountStateFromUserInfo(account_id, user_info);
435 DeleteFetcher(fetcher);
438 void AccountTrackerService::OnUserInfoFetchFailure(
439 AccountInfoFetcher* fetcher) {
440 LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_id();
441 NotifyAccountUpdateFailed(fetcher->account_id());
442 DeleteFetcher(fetcher);
443 // TODO(rogerta): figure out when to retry.
446 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher* fetcher) {
447 DVLOG(1) << "DeleteFetcher " << fetcher->account_id();
448 const std::string& account_id = fetcher->account_id();
449 DCHECK(ContainsKey(user_info_requests_, account_id));
450 DCHECK_EQ(fetcher, user_info_requests_[account_id]);
451 user_info_requests_.erase(account_id);
452 delete fetcher;
455 void AccountTrackerService::LoadFromPrefs() {
456 const base::ListValue* list =
457 signin_client_->GetPrefs()->GetList(kAccountInfoPref);
458 for (size_t i = 0; i < list->GetSize(); ++i) {
459 const base::DictionaryValue* dict;
460 if (list->GetDictionary(i, &dict)) {
461 base::string16 value;
462 if (dict->GetString(kAccountKeyPath, &value)) {
463 std::string account_id = base::UTF16ToUTF8(value);
464 StartTrackingAccount(account_id);
465 AccountState& state = accounts_[account_id];
467 if (dict->GetString(kAccountGaiaPath, &value))
468 state.info.gaia = base::UTF16ToUTF8(value);
469 if (dict->GetString(kAccountEmailPath, &value))
470 state.info.email = base::UTF16ToUTF8(value);
471 if (dict->GetString(kAccountHostedDomainPath, &value))
472 state.info.hosted_domain = base::UTF16ToUTF8(value);
473 if (!state.info.IsValid())
474 NotifyAccountUpdated(state);
480 void AccountTrackerService::SaveToPrefs(const AccountState& state) {
481 if (!signin_client_->GetPrefs())
482 return;
484 base::DictionaryValue* dict = NULL;
485 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id);
486 ListPrefUpdate update(signin_client_->GetPrefs(), kAccountInfoPref);
487 for(size_t i = 0; i < update->GetSize(); ++i, dict = NULL) {
488 if (update->GetDictionary(i, &dict)) {
489 base::string16 value;
490 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16)
491 break;
495 if (!dict) {
496 dict = new base::DictionaryValue();
497 update->Append(dict); // |update| takes ownership.
498 dict->SetString(kAccountKeyPath, account_id_16);
501 dict->SetString(kAccountEmailPath, state.info.email);
502 dict->SetString(kAccountGaiaPath, state.info.gaia);
503 dict->SetString(kAccountHostedDomainPath, state.info.hosted_domain);
506 void AccountTrackerService::RemoveFromPrefs(const AccountState& state) {
507 if (!signin_client_->GetPrefs())
508 return;
510 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id);
511 ListPrefUpdate update(signin_client_->GetPrefs(), kAccountInfoPref);
512 for(size_t i = 0; i < update->GetSize(); ++i) {
513 base::DictionaryValue* dict = NULL;
514 if (update->GetDictionary(i, &dict)) {
515 base::string16 value;
516 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) {
517 update->Remove(i, NULL);
518 break;
524 void AccountTrackerService::LoadFromTokenService() {
525 std::vector<std::string> accounts = token_service_->GetAccounts();
526 for (std::vector<std::string>::const_iterator it = accounts.begin();
527 it != accounts.end(); ++it) {
528 OnRefreshTokenAvailable(*it);
532 void AccountTrackerService::SendRefreshTokenAnnotationRequest(
533 const std::string& account_id) {
534 // We only need to send RefreshTokenAnnotationRequest from desktop platforms.
535 #if !defined(OS_ANDROID) && !defined(OS_IOS)
536 if (IsRefreshTokenDeviceIdExperimentEnabled() ||
537 base::CommandLine::ForCurrentProcess()->HasSwitch(
538 switches::kEnableRefreshTokenAnnotationRequest)) {
539 scoped_ptr<RefreshTokenAnnotationRequest> request =
540 RefreshTokenAnnotationRequest::SendIfNeeded(
541 signin_client_->GetPrefs(), token_service_, signin_client_,
542 signin_client_->GetURLRequestContext(), account_id,
543 base::Bind(
544 &AccountTrackerService::RefreshTokenAnnotationRequestDone,
545 base::Unretained(this), account_id));
546 // If request was sent AccountTrackerService needs to own request till it
547 // finishes.
548 if (request)
549 refresh_token_annotation_requests_.set(account_id, request.Pass());
551 #endif
554 void AccountTrackerService::RefreshTokenAnnotationRequestDone(
555 const std::string& account_id) {
556 refresh_token_annotation_requests_.erase(account_id);
559 std::string AccountTrackerService::PickAccountIdForAccount(
560 const std::string& gaia,
561 const std::string& email) {
562 return PickAccountIdForAccount(signin_client_->GetPrefs(), gaia, email);
565 // static
566 std::string AccountTrackerService::PickAccountIdForAccount(
567 PrefService* pref_service,
568 const std::string& gaia,
569 const std::string& email) {
570 DCHECK(!gaia.empty());
571 DCHECK(!email.empty());
572 switch(GetMigrationState(pref_service)) {
573 case MIGRATION_NOT_STARTED:
574 case MIGRATION_IN_PROGRESS:
575 // Some tests don't use a real email address. To support these cases,
576 // don't try to canonicalize these strings.
577 return (email.find('@') == std::string::npos) ? email :
578 gaia::CanonicalizeEmail(email);
579 case MIGRATION_DONE:
580 return gaia;
581 default:
582 NOTREACHED();
583 return email;
587 void AccountTrackerService::SeedAccountInfo(const std::string& gaia,
588 const std::string& email) {
589 DVLOG(1) << "AccountTrackerService::SeedAccountInfo"
590 << " gaia_id=" << gaia
591 << " email=" << email;
593 DCHECK(!gaia.empty());
594 DCHECK(!email.empty());
595 const std::string account_id = PickAccountIdForAccount(gaia, email);
596 const bool already_exists = ContainsKey(accounts_, account_id);
597 StartTrackingAccount(account_id);
598 AccountState& state = accounts_[account_id];
599 DCHECK(!already_exists || state.info.gaia == gaia);
600 state.info.gaia = gaia;
601 state.info.email = email;
602 SaveToPrefs(state);