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/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/signin/core/browser/signin_manager.h"
14 #include "components/signin/core/common/signin_pref_names.h"
15 #include "google_apis/gaia/gaia_auth_util.h"
16 #include "google_apis/gaia/gaia_constants.h"
17 #include "google_apis/gaia/gaia_oauth_client.h"
18 #include "google_apis/gaia/oauth2_token_service.h"
19 #include "net/url_request/url_request_context_getter.h"
23 const char kAccountKeyPath
[] = "account_id";
24 const char kAccountEmailPath
[] = "email";
25 const char kAccountGaiaPath
[] = "gaia";
29 class AccountInfoFetcher
: public OAuth2TokenService::Consumer
,
30 public gaia::GaiaOAuthClient::Delegate
{
32 AccountInfoFetcher(OAuth2TokenService
* token_service
,
33 net::URLRequestContextGetter
* request_context_getter
,
34 AccountTrackerService
* service
,
35 const std::string
& account_id
);
36 ~AccountInfoFetcher() override
;
38 const std::string
& account_id() { return account_id_
; }
42 // OAuth2TokenService::Consumer implementation.
43 void OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
44 const std::string
& access_token
,
45 const base::Time
& expiration_time
) override
;
46 void OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
47 const GoogleServiceAuthError
& error
) override
;
49 // gaia::GaiaOAuthClient::Delegate implementation.
50 void OnGetUserInfoResponse(
51 scoped_ptr
<base::DictionaryValue
> user_info
) override
;
52 void OnOAuthError() override
;
53 void OnNetworkError(int response_code
) override
;
56 OAuth2TokenService
* token_service_
;
57 net::URLRequestContextGetter
* request_context_getter_
;
58 AccountTrackerService
* service_
;
59 const std::string account_id_
;
61 scoped_ptr
<OAuth2TokenService::Request
> login_token_request_
;
62 scoped_ptr
<gaia::GaiaOAuthClient
> gaia_oauth_client_
;
65 AccountInfoFetcher::AccountInfoFetcher(
66 OAuth2TokenService
* token_service
,
67 net::URLRequestContextGetter
* request_context_getter
,
68 AccountTrackerService
* service
,
69 const std::string
& account_id
)
70 : OAuth2TokenService::Consumer("gaia_account_tracker"),
71 token_service_(token_service
),
72 request_context_getter_(request_context_getter
),
74 account_id_(account_id
) {
75 TRACE_EVENT_ASYNC_BEGIN1(
76 "AccountTrackerService", "AccountIdFetcher", this,
77 "account_id", account_id
);
80 AccountInfoFetcher::~AccountInfoFetcher() {
81 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this);
84 void AccountInfoFetcher::Start() {
85 OAuth2TokenService::ScopeSet scopes
;
86 scopes
.insert(GaiaConstants::kGoogleUserInfoEmail
);
87 scopes
.insert(GaiaConstants::kGoogleUserInfoProfile
);
88 login_token_request_
= token_service_
->StartRequest(
89 account_id_
, scopes
, this);
92 void AccountInfoFetcher::OnGetTokenSuccess(
93 const OAuth2TokenService::Request
* request
,
94 const std::string
& access_token
,
95 const base::Time
& expiration_time
) {
96 TRACE_EVENT_ASYNC_STEP_PAST0(
97 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess");
98 DCHECK_EQ(request
, login_token_request_
.get());
100 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(request_context_getter_
));
102 const int kMaxRetries
= 3;
103 gaia_oauth_client_
->GetUserInfo(access_token
, kMaxRetries
, this);
106 void AccountInfoFetcher::OnGetTokenFailure(
107 const OAuth2TokenService::Request
* request
,
108 const GoogleServiceAuthError
& error
) {
109 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
113 "google_service_auth_error",
115 LOG(ERROR
) << "OnGetTokenFailure: " << error
.ToString();
116 DCHECK_EQ(request
, login_token_request_
.get());
117 service_
->OnUserInfoFetchFailure(this);
120 void AccountInfoFetcher::OnGetUserInfoResponse(
121 scoped_ptr
<base::DictionaryValue
> user_info
) {
122 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
125 "OnGetUserInfoResponse",
128 service_
->OnUserInfoFetchSuccess(this, user_info
.get());
131 void AccountInfoFetcher::OnOAuthError() {
132 TRACE_EVENT_ASYNC_STEP_PAST0(
133 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError");
134 LOG(ERROR
) << "OnOAuthError";
135 service_
->OnUserInfoFetchFailure(this);
138 void AccountInfoFetcher::OnNetworkError(int response_code
) {
139 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
145 LOG(ERROR
) << "OnNetworkError " << response_code
;
146 service_
->OnUserInfoFetchFailure(this);
150 const char AccountTrackerService::kAccountInfoPref
[] = "account_info";
152 AccountTrackerService::AccountTrackerService()
153 : token_service_(NULL
),
155 shutdown_called_(false) {
158 AccountTrackerService::~AccountTrackerService() {
159 DCHECK(shutdown_called_
);
162 void AccountTrackerService::Initialize(
163 OAuth2TokenService
* token_service
,
164 PrefService
* pref_service
,
165 net::URLRequestContextGetter
* request_context_getter
) {
166 DCHECK(token_service
);
167 DCHECK(!token_service_
);
168 DCHECK(pref_service
);
169 DCHECK(!pref_service_
);
170 token_service_
= token_service
;
171 pref_service_
= pref_service
;
172 request_context_getter_
= request_context_getter
;
173 token_service_
->AddObserver(this);
175 LoadFromTokenService();
178 void AccountTrackerService::Shutdown() {
179 shutdown_called_
= true;
180 STLDeleteValues(&user_info_requests_
);
181 token_service_
->RemoveObserver(this);
184 void AccountTrackerService::AddObserver(Observer
* observer
) {
185 observer_list_
.AddObserver(observer
);
188 void AccountTrackerService::RemoveObserver(Observer
* observer
) {
189 observer_list_
.RemoveObserver(observer
);
192 bool AccountTrackerService::IsAllUserInfoFetched() const {
193 return user_info_requests_
.empty();
196 std::vector
<AccountTrackerService::AccountInfo
>
197 AccountTrackerService::GetAccounts() const {
198 std::vector
<AccountInfo
> accounts
;
200 for (std::map
<std::string
, AccountState
>::const_iterator it
=
202 it
!= accounts_
.end();
204 const AccountState
& state
= it
->second
;
205 accounts
.push_back(state
.info
);
210 AccountTrackerService::AccountInfo
AccountTrackerService::GetAccountInfo(
211 const std::string
& account_id
) {
212 if (ContainsKey(accounts_
, account_id
))
213 return accounts_
[account_id
].info
;
215 return AccountInfo();
218 AccountTrackerService::AccountInfo
219 AccountTrackerService::FindAccountInfoByGaiaId(
220 const std::string
& gaia_id
) {
221 for (std::map
<std::string
, AccountState
>::const_iterator it
=
223 it
!= accounts_
.end();
225 const AccountState
& state
= it
->second
;
226 if (state
.info
.gaia
== gaia_id
)
230 return AccountInfo();
233 AccountTrackerService::AccountInfo
234 AccountTrackerService::FindAccountInfoByEmail(
235 const std::string
& email
) {
236 for (std::map
<std::string
, AccountState
>::const_iterator it
=
238 it
!= accounts_
.end();
240 const AccountState
& state
= it
->second
;
241 if (gaia::AreEmailsSame(state
.info
.email
, email
))
245 return AccountInfo();
248 AccountTrackerService::AccountIdMigrationState
249 AccountTrackerService::GetMigrationState() {
250 return GetMigrationState(pref_service_
);
254 AccountTrackerService::AccountIdMigrationState
255 AccountTrackerService::GetMigrationState(PrefService
* pref_service
) {
256 return static_cast<AccountTrackerService::AccountIdMigrationState
>(
257 pref_service
->GetInteger(prefs::kAccountIdMigrationState
));
260 void AccountTrackerService::OnRefreshTokenAvailable(
261 const std::string
& account_id
) {
262 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422460 is fixed.
263 tracked_objects::ScopedTracker
tracking_profile(
264 FROM_HERE_WITH_EXPLICIT_FUNCTION(
265 "422460 AccountTrackerService::OnRefreshTokenAvailable"));
267 TRACE_EVENT1("AccountTrackerService",
268 "AccountTracker::OnRefreshTokenAvailable",
271 DVLOG(1) << "AVAILABLE " << account_id
;
273 StartTrackingAccount(account_id
);
274 AccountState
& state
= accounts_
[account_id
];
276 if (state
.info
.gaia
.empty())
277 StartFetchingUserInfo(account_id
);
280 void AccountTrackerService::OnRefreshTokenRevoked(
281 const std::string
& account_id
) {
282 TRACE_EVENT1("AccountTrackerService",
283 "AccountTracker::OnRefreshTokenRevoked",
287 DVLOG(1) << "REVOKED " << account_id
;
288 StopTrackingAccount(account_id
);
291 void AccountTrackerService::NotifyAccountUpdated(const AccountState
& state
) {
292 DCHECK(!state
.info
.gaia
.empty());
294 Observer
, observer_list_
, OnAccountUpdated(state
.info
));
297 void AccountTrackerService::NotifyAccountRemoved(const AccountState
& state
) {
298 DCHECK(!state
.info
.gaia
.empty());
300 Observer
, observer_list_
, OnAccountRemoved(state
.info
));
303 void AccountTrackerService::StartTrackingAccount(
304 const std::string
& account_id
) {
305 if (!ContainsKey(accounts_
, account_id
)) {
306 DVLOG(1) << "StartTracking " << account_id
;
308 state
.info
.account_id
= account_id
;
309 accounts_
.insert(make_pair(account_id
, state
));
313 void AccountTrackerService::StopTrackingAccount(const std::string
& account_id
) {
314 DVLOG(1) << "StopTracking " << account_id
;
315 if (ContainsKey(accounts_
, account_id
)) {
316 AccountState
& state
= accounts_
[account_id
];
317 RemoveFromPrefs(state
);
318 if (!state
.info
.gaia
.empty())
319 NotifyAccountRemoved(state
);
321 accounts_
.erase(account_id
);
324 if (ContainsKey(user_info_requests_
, account_id
))
325 DeleteFetcher(user_info_requests_
[account_id
]);
328 void AccountTrackerService::StartFetchingUserInfo(
329 const std::string
& account_id
) {
330 if (ContainsKey(user_info_requests_
, account_id
))
331 DeleteFetcher(user_info_requests_
[account_id
]);
333 DVLOG(1) << "StartFetching " << account_id
;
334 AccountInfoFetcher
* fetcher
=
335 new AccountInfoFetcher(token_service_
,
336 request_context_getter_
.get(),
339 user_info_requests_
[account_id
] = fetcher
;
343 void AccountTrackerService::OnUserInfoFetchSuccess(
344 AccountInfoFetcher
* fetcher
,
345 const base::DictionaryValue
* user_info
) {
346 const std::string
& account_id
= fetcher
->account_id();
347 DCHECK(ContainsKey(accounts_
, account_id
));
348 AccountState
& state
= accounts_
[account_id
];
352 if (user_info
->GetString("id", &gaia_id
) &&
353 user_info
->GetString("email", &email
)) {
354 state
.info
.gaia
= gaia_id
;
355 state
.info
.email
= email
;
357 NotifyAccountUpdated(state
);
360 DeleteFetcher(fetcher
);
363 void AccountTrackerService::OnUserInfoFetchFailure(
364 AccountInfoFetcher
* fetcher
) {
365 LOG(WARNING
) << "Failed to get UserInfo for " << fetcher
->account_id();
366 DeleteFetcher(fetcher
);
367 // TODO(rogerta): figure out when to retry.
370 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher
* fetcher
) {
371 DVLOG(1) << "DeleteFetcher " << fetcher
->account_id();
372 const std::string
& account_id
= fetcher
->account_id();
373 DCHECK(ContainsKey(user_info_requests_
, account_id
));
374 DCHECK_EQ(fetcher
, user_info_requests_
[account_id
]);
375 user_info_requests_
.erase(account_id
);
379 void AccountTrackerService::LoadFromPrefs() {
380 const base::ListValue
* list
= pref_service_
->GetList(kAccountInfoPref
);
381 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
382 const base::DictionaryValue
* dict
;
383 if (list
->GetDictionary(i
, &dict
)) {
384 base::string16 value
;
385 if (dict
->GetString(kAccountKeyPath
, &value
)) {
386 std::string account_id
= base::UTF16ToUTF8(value
);
387 StartTrackingAccount(account_id
);
388 AccountState
& state
= accounts_
[account_id
];
390 if (dict
->GetString(kAccountGaiaPath
, &value
))
391 state
.info
.gaia
= base::UTF16ToUTF8(value
);
392 if (dict
->GetString(kAccountEmailPath
, &value
))
393 state
.info
.email
= base::UTF16ToUTF8(value
);
395 if (!state
.info
.gaia
.empty())
396 NotifyAccountUpdated(state
);
402 void AccountTrackerService::SaveToPrefs(const AccountState
& state
) {
406 base::DictionaryValue
* dict
= NULL
;
407 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
408 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
409 for(size_t i
= 0; i
< update
->GetSize(); ++i
, dict
= NULL
) {
410 if (update
->GetDictionary(i
, &dict
)) {
411 base::string16 value
;
412 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
)
418 dict
= new base::DictionaryValue();
419 update
->Append(dict
); // |update| takes ownership.
420 dict
->SetString(kAccountKeyPath
, account_id_16
);
423 dict
->SetString(kAccountEmailPath
, state
.info
.email
);
424 dict
->SetString(kAccountGaiaPath
, state
.info
.gaia
);
427 void AccountTrackerService::RemoveFromPrefs(const AccountState
& state
) {
431 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
432 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
433 for(size_t i
= 0; i
< update
->GetSize(); ++i
) {
434 base::DictionaryValue
* dict
= NULL
;
435 if (update
->GetDictionary(i
, &dict
)) {
436 base::string16 value
;
437 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
) {
438 update
->Remove(i
, NULL
);
445 void AccountTrackerService::LoadFromTokenService() {
446 std::vector
<std::string
> accounts
= token_service_
->GetAccounts();
447 for (std::vector
<std::string
>::const_iterator it
= accounts
.begin();
448 it
!= accounts
.end(); ++it
) {
449 OnRefreshTokenAvailable(*it
);
453 std::string
AccountTrackerService::PickAccountIdForAccount(
454 const std::string
& gaia
,
455 const std::string
& email
) {
456 return PickAccountIdForAccount(pref_service_
, gaia
, email
);
460 std::string
AccountTrackerService::PickAccountIdForAccount(
461 PrefService
* pref_service
,
462 const std::string
& gaia
,
463 const std::string
& email
) {
464 DCHECK(!gaia
.empty());
465 DCHECK(!email
.empty());
466 switch(GetMigrationState(pref_service
)) {
467 case MIGRATION_NOT_STARTED
:
468 case MIGRATION_IN_PROGRESS
:
469 // Some tests don't use a real email address. To support these cases,
470 // don't try to canonicalize these strings.
471 return (email
.find('@') == std::string::npos
) ? email
:
472 gaia::CanonicalizeEmail(email
);
481 void AccountTrackerService::SeedAccountInfo(const std::string
& gaia
,
482 const std::string
& email
) {
483 DVLOG(1) << "AccountTrackerService::SeedAccountInfo"
484 << " gaia_id=" << gaia
485 << " email=" << email
;
487 DCHECK(!gaia
.empty());
488 DCHECK(!email
.empty());
489 const std::string account_id
= PickAccountIdForAccount(gaia
, email
);
490 const bool already_exists
= ContainsKey(accounts_
, account_id
);
491 StartTrackingAccount(account_id
);
492 AccountState
& state
= accounts_
[account_id
];
493 DCHECK(!already_exists
|| state
.info
.gaia
== gaia
);
494 state
.info
.gaia
= gaia
;
495 state
.info
.email
= email
;