1 // Copyright (c) 2012 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/about_signin_internals.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <tuple>
11
12 #include "base/command_line.h"
13 #include "base/hash/hash.h"
14 #include "base/logging.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time_to_iso8601.h"
17 #include "base/trace_event/trace_event.h"
18 #include "build/build_config.h"
19 #include "components/prefs/pref_registry_simple.h"
20 #include "components/prefs/pref_service.h"
21 #include "components/signin/core/browser/account_reconcilor.h"
22 #include "components/signin/public/base/signin_client.h"
23 #include "components/signin/public/base/signin_switches.h"
24 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
25 #include "components/signin/public/identity_manager/diagnostics_provider.h"
26 #include "components/signin/public/identity_manager/identity_manager.h"
27 #include "components/signin/public/identity_manager/load_credentials_state.h"
28 #include "net/base/backoff_entry.h"
29
30 namespace {
31
32 // The maximum number of the refresh token events. Only the last
33 // |kMaxRefreshTokenListSize| events are kept in memory.
34 const size_t kMaxRefreshTokenListSize = 50;
35
36 enum class GaiaCookiesState {
37 kAllowed,
38 kClearOnExit,
39 kBlocked,
40 };
41
GetGaiaCookiesState(SigninClient * signin_client)42 GaiaCookiesState GetGaiaCookiesState(SigninClient* signin_client) {
43 bool signin_cookies_allowed = signin_client->AreSigninCookiesAllowed();
44 if (!signin_cookies_allowed)
45 return GaiaCookiesState::kBlocked;
46
47 bool clear_cookies_on_exit = signin_client->AreSigninCookiesDeletedOnExit();
48 if (clear_cookies_on_exit)
49 return GaiaCookiesState::kClearOnExit;
50
51 return GaiaCookiesState::kAllowed;
52 }
53
GetGaiaCookiesStateAsString(const GaiaCookiesState state)54 std::string GetGaiaCookiesStateAsString(const GaiaCookiesState state) {
55 switch (state) {
56 case GaiaCookiesState::kBlocked:
57 return "Not allowed";
58 case GaiaCookiesState::kClearOnExit:
59 return "Cleared on exit";
60 case GaiaCookiesState::kAllowed:
61 return "Allowed";
62 }
63 }
64
AddSection(base::ListValue * parent_list,const std::string & title)65 base::ListValue* AddSection(base::ListValue* parent_list,
66 const std::string& title) {
67 auto section = std::make_unique<base::DictionaryValue>();
68
69 section->SetString("title", title);
70 base::ListValue* section_contents =
71 section->SetList("data", std::make_unique<base::ListValue>());
72 parent_list->Append(std::move(section));
73 return section_contents;
74 }
75
AddSectionEntry(base::ListValue * section_list,const std::string & field_name,const std::string & field_status,const std::string & field_time="")76 void AddSectionEntry(base::ListValue* section_list,
77 const std::string& field_name,
78 const std::string& field_status,
79 const std::string& field_time = "") {
80 std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
81 entry->SetString("label", field_name);
82 entry->SetString("status", field_status);
83 entry->SetString("time", field_time);
84 section_list->Append(std::move(entry));
85 }
86
AddCookieEntry(base::ListValue * accounts_list,const std::string & field_email,const std::string & field_gaia_id,const std::string & field_valid)87 void AddCookieEntry(base::ListValue* accounts_list,
88 const std::string& field_email,
89 const std::string& field_gaia_id,
90 const std::string& field_valid) {
91 std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
92 entry->SetString("email", field_email);
93 entry->SetString("gaia_id", field_gaia_id);
94 entry->SetString("valid", field_valid);
95 accounts_list->Append(std::move(entry));
96 }
97
SigninStatusFieldToLabel(signin_internals_util::UntimedSigninStatusField field)98 std::string SigninStatusFieldToLabel(
99 signin_internals_util::UntimedSigninStatusField field) {
100 switch (field) {
101 case signin_internals_util::ACCOUNT_ID:
102 return "Account Id";
103 case signin_internals_util::GAIA_ID:
104 return "Gaia Id";
105 case signin_internals_util::USERNAME:
106 return "Username";
107 case signin_internals_util::UNTIMED_FIELDS_END:
108 NOTREACHED();
109 return std::string();
110 }
111 NOTREACHED();
112 return std::string();
113 }
114
TokenServiceLoadCredentialsStateToLabel(signin::LoadCredentialsState state)115 std::string TokenServiceLoadCredentialsStateToLabel(
116 signin::LoadCredentialsState state) {
117 switch (state) {
118 case signin::LoadCredentialsState::LOAD_CREDENTIALS_NOT_STARTED:
119 return "Load credentials not started";
120 case signin::LoadCredentialsState::LOAD_CREDENTIALS_IN_PROGRESS:
121 return "Load credentials in progress";
122 case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS:
123 return "Load credentials finished with success";
124 case signin::LoadCredentialsState::
125 LOAD_CREDENTIALS_FINISHED_WITH_DB_CANNOT_BE_OPENED:
126 return "Load credentials failed with datase cannot be opened error";
127 case signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS:
128 return "Load credentials failed with database errors";
129 case signin::LoadCredentialsState::
130 LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS:
131 return "Load credentials failed with decrypt errors";
132 case signin::LoadCredentialsState::
133 LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT:
134 return "Load credentials failed with no refresh token for signed in "
135 "account";
136 case signin::LoadCredentialsState::
137 LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS:
138 return "Load credentials failed with unknown errors";
139 }
140 NOTREACHED();
141 return std::string();
142 }
143
144 #if !defined (OS_CHROMEOS)
SigninStatusFieldToLabel(signin_internals_util::TimedSigninStatusField field)145 std::string SigninStatusFieldToLabel(
146 signin_internals_util::TimedSigninStatusField field) {
147 switch (field) {
148 case signin_internals_util::AUTHENTICATION_RESULT_RECEIVED:
149 return "Gaia Authentication Result";
150 case signin_internals_util::REFRESH_TOKEN_RECEIVED:
151 return "RefreshToken Received";
152 case signin_internals_util::TIMED_FIELDS_END:
153 NOTREACHED();
154 return "Error";
155 }
156 NOTREACHED();
157 return "Error";
158 }
159 #endif // !defined (OS_CHROMEOS)
160
161 // It's quite unfortunate that |time| is saved in prefs as a string instead of
162 // base::Time because any change of the format would create inconsistency.
SetPref(PrefService * prefs,signin_internals_util::TimedSigninStatusField field,const std::string & value,const std::string & time)163 void SetPref(PrefService* prefs,
164 signin_internals_util::TimedSigninStatusField field,
165 const std::string& value,
166 const std::string& time) {
167 std::string value_pref = SigninStatusFieldToString(field) + ".value";
168 std::string time_pref = SigninStatusFieldToString(field) + ".time";
169 prefs->SetString(value_pref, value);
170 prefs->SetString(time_pref, time);
171 }
172
GetPref(PrefService * prefs,signin_internals_util::TimedSigninStatusField field,std::string * value,std::string * time)173 void GetPref(PrefService* prefs,
174 signin_internals_util::TimedSigninStatusField field,
175 std::string* value,
176 std::string* time) {
177 std::string value_pref = SigninStatusFieldToString(field) + ".value";
178 std::string time_pref = SigninStatusFieldToString(field) + ".time";
179 *value = prefs->GetString(value_pref);
180 *time = prefs->GetString(time_pref);
181 }
182
ClearPref(PrefService * prefs,signin_internals_util::TimedSigninStatusField field)183 void ClearPref(PrefService* prefs,
184 signin_internals_util::TimedSigninStatusField field) {
185 std::string value_pref = SigninStatusFieldToString(field) + ".value";
186 std::string time_pref = SigninStatusFieldToString(field) + ".time";
187 prefs->ClearPref(value_pref);
188 prefs->ClearPref(time_pref);
189 }
190
GetAccountConsistencyDescription(signin::AccountConsistencyMethod method)191 std::string GetAccountConsistencyDescription(
192 signin::AccountConsistencyMethod method) {
193 switch (method) {
194 case signin::AccountConsistencyMethod::kDisabled:
195 return "None";
196 case signin::AccountConsistencyMethod::kMirror:
197 return "Mirror";
198 case signin::AccountConsistencyMethod::kDice:
199 return "DICE";
200 }
201 NOTREACHED();
202 return "";
203 }
204
205 } // anonymous namespace
206
AboutSigninInternals(signin::IdentityManager * identity_manager,SigninErrorController * signin_error_controller,signin::AccountConsistencyMethod account_consistency,SigninClient * client,AccountReconcilor * account_reconcilor)207 AboutSigninInternals::AboutSigninInternals(
208 signin::IdentityManager* identity_manager,
209 SigninErrorController* signin_error_controller,
210 signin::AccountConsistencyMethod account_consistency,
211 SigninClient* client,
212 AccountReconcilor* account_reconcilor)
213 : identity_manager_(identity_manager),
214 client_(client),
215 signin_error_controller_(signin_error_controller),
216 account_reconcilor_(account_reconcilor),
217 account_consistency_(account_consistency) {
218 RefreshSigninPrefs();
219 client_->AddContentSettingsObserver(this);
220 signin_error_controller_->AddObserver(this);
221 identity_manager_->AddObserver(this);
222 identity_manager_->AddDiagnosticsObserver(this);
223 account_reconcilor_->AddObserver(this);
224 }
225
~AboutSigninInternals()226 AboutSigninInternals::~AboutSigninInternals() {}
227
operator ++(signin_internals_util::UntimedSigninStatusField & field)228 signin_internals_util::UntimedSigninStatusField& operator++(
229 signin_internals_util::UntimedSigninStatusField& field) {
230 field =
231 static_cast<signin_internals_util::UntimedSigninStatusField>(field + 1);
232 return field;
233 }
234
operator ++(signin_internals_util::TimedSigninStatusField & field)235 signin_internals_util::TimedSigninStatusField& operator++(
236 signin_internals_util::TimedSigninStatusField& field) {
237 field = static_cast<signin_internals_util::TimedSigninStatusField>(field + 1);
238 return field;
239 }
240
241 // static
RegisterPrefs(PrefRegistrySimple * user_prefs)242 void AboutSigninInternals::RegisterPrefs(PrefRegistrySimple* user_prefs) {
243 // TODO(rogerta): leaving untimed fields here for now because legacy
244 // profiles still have these prefs. In three or four version from M43
245 // we can probably remove them.
246 for (signin_internals_util::UntimedSigninStatusField i =
247 signin_internals_util::UNTIMED_FIELDS_BEGIN;
248 i < signin_internals_util::UNTIMED_FIELDS_END; ++i) {
249 const std::string pref_path = SigninStatusFieldToString(i);
250 user_prefs->RegisterStringPref(pref_path, std::string());
251 }
252
253 for (signin_internals_util::TimedSigninStatusField i =
254 signin_internals_util::TIMED_FIELDS_BEGIN;
255 i < signin_internals_util::TIMED_FIELDS_END; ++i) {
256 const std::string value = SigninStatusFieldToString(i) + ".value";
257 const std::string time = SigninStatusFieldToString(i) + ".time";
258 user_prefs->RegisterStringPref(value, std::string());
259 user_prefs->RegisterStringPref(time, std::string());
260 }
261 }
262
AddSigninObserver(AboutSigninInternals::Observer * observer)263 void AboutSigninInternals::AddSigninObserver(
264 AboutSigninInternals::Observer* observer) {
265 signin_observers_.AddObserver(observer);
266 }
267
RemoveSigninObserver(AboutSigninInternals::Observer * observer)268 void AboutSigninInternals::RemoveSigninObserver(
269 AboutSigninInternals::Observer* observer) {
270 signin_observers_.RemoveObserver(observer);
271 }
272
NotifyTimedSigninFieldValueChanged(const signin_internals_util::TimedSigninStatusField & field,const std::string & value)273 void AboutSigninInternals::NotifyTimedSigninFieldValueChanged(
274 const signin_internals_util::TimedSigninStatusField& field,
275 const std::string& value) {
276 unsigned int field_index = field - signin_internals_util::TIMED_FIELDS_BEGIN;
277 DCHECK(field_index >= 0 &&
278 field_index < signin_status_.timed_signin_fields.size());
279
280 base::Time now = base::Time::NowFromSystemTime();
281 std::string time_as_str = base::TimeToISO8601(now);
282 TimedSigninStatusValue timed_value(value, time_as_str);
283
284 signin_status_.timed_signin_fields[field_index] = timed_value;
285
286 // Also persist these values in the prefs.
287 SetPref(client_->GetPrefs(), field, value, time_as_str);
288
289 // If the user is restarting a sign in process, clear the fields that are
290 // to come.
291 if (field == signin_internals_util::AUTHENTICATION_RESULT_RECEIVED) {
292 ClearPref(client_->GetPrefs(),
293 signin_internals_util::REFRESH_TOKEN_RECEIVED);
294 }
295
296 NotifyObservers();
297 }
298
RefreshSigninPrefs()299 void AboutSigninInternals::RefreshSigninPrefs() {
300 // Return if no client exists. Can occur in unit tests.
301 if (!client_)
302 return;
303
304 PrefService* pref_service = client_->GetPrefs();
305 for (signin_internals_util::TimedSigninStatusField i =
306 signin_internals_util::TIMED_FIELDS_BEGIN;
307 i < signin_internals_util::TIMED_FIELDS_END; ++i) {
308 std::string time_str;
309 std::string value_str;
310 GetPref(pref_service, i, &value_str, &time_str);
311 TimedSigninStatusValue value(value_str, time_str);
312 signin_status_
313 .timed_signin_fields[i - signin_internals_util::TIMED_FIELDS_BEGIN] =
314 value;
315 }
316
317 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
318
319 NotifyObservers();
320 }
321
Shutdown()322 void AboutSigninInternals::Shutdown() {
323 client_->RemoveContentSettingsObserver(this);
324 signin_error_controller_->RemoveObserver(this);
325 identity_manager_->RemoveObserver(this);
326 identity_manager_->RemoveDiagnosticsObserver(this);
327 account_reconcilor_->RemoveObserver(this);
328 }
329
OnContentSettingChanged(const ContentSettingsPattern & primary_pattern,const ContentSettingsPattern & secondary_pattern,ContentSettingsType content_type)330 void AboutSigninInternals::OnContentSettingChanged(
331 const ContentSettingsPattern& primary_pattern,
332 const ContentSettingsPattern& secondary_pattern,
333 ContentSettingsType content_type) {
334 // If this is not a change to cookie settings, just ignore.
335 if (content_type != ContentSettingsType::COOKIES)
336 return;
337
338 NotifyObservers();
339 }
340
NotifyObservers()341 void AboutSigninInternals::NotifyObservers() {
342 if (!signin_observers_.might_have_observers())
343 return;
344
345 std::unique_ptr<base::DictionaryValue> signin_status_value =
346 signin_status_.ToValue(identity_manager_, signin_error_controller_,
347 client_, account_consistency_,
348 account_reconcilor_);
349
350 for (auto& observer : signin_observers_)
351 observer.OnSigninStateChanged(signin_status_value.get());
352 }
353
GetSigninStatus()354 std::unique_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
355 return signin_status_.ToValue(identity_manager_, signin_error_controller_,
356 client_, account_consistency_,
357 account_reconcilor_);
358 }
359
OnAccessTokenRequested(const CoreAccountId & account_id,const std::string & consumer_id,const signin::ScopeSet & scopes)360 void AboutSigninInternals::OnAccessTokenRequested(
361 const CoreAccountId& account_id,
362 const std::string& consumer_id,
363 const signin::ScopeSet& scopes) {
364 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
365 if (token) {
366 *token = TokenInfo(consumer_id, scopes);
367 } else {
368 signin_status_.token_info_map[account_id].push_back(
369 std::make_unique<TokenInfo>(consumer_id, scopes));
370 }
371
372 NotifyObservers();
373 }
374
OnAccessTokenRequestCompleted(const CoreAccountId & account_id,const std::string & consumer_id,const signin::ScopeSet & scopes,GoogleServiceAuthError error,base::Time expiration_time)375 void AboutSigninInternals::OnAccessTokenRequestCompleted(
376 const CoreAccountId& account_id,
377 const std::string& consumer_id,
378 const signin::ScopeSet& scopes,
379 GoogleServiceAuthError error,
380 base::Time expiration_time) {
381 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
382 if (!token) {
383 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
384 return;
385 }
386
387 token->receive_time = base::Time::Now();
388 token->error = error;
389 token->expiration_time = expiration_time;
390
391 NotifyObservers();
392 }
393
OnRefreshTokenUpdatedForAccountFromSource(const CoreAccountId & account_id,bool is_refresh_token_valid,const std::string & source)394 void AboutSigninInternals::OnRefreshTokenUpdatedForAccountFromSource(
395 const CoreAccountId& account_id,
396 bool is_refresh_token_valid,
397 const std::string& source) {
398 RefreshTokenEvent event;
399 event.account_id = account_id;
400 event.type =
401 is_refresh_token_valid
402 ? AboutSigninInternals::RefreshTokenEventType::kUpdateToRegular
403 : AboutSigninInternals::RefreshTokenEventType::kUpdateToInvalid;
404 event.source = source;
405 signin_status_.AddRefreshTokenEvent(event);
406 }
407
OnRefreshTokenRemovedForAccountFromSource(const CoreAccountId & account_id,const std::string & source)408 void AboutSigninInternals::OnRefreshTokenRemovedForAccountFromSource(
409 const CoreAccountId& account_id,
410 const std::string& source) {
411 RefreshTokenEvent event;
412 event.account_id = account_id;
413 event.type = AboutSigninInternals::RefreshTokenEventType::kRevokeRegular;
414 event.source = source;
415 signin_status_.AddRefreshTokenEvent(event);
416 }
417
OnRefreshTokensLoaded()418 void AboutSigninInternals::OnRefreshTokensLoaded() {
419 RefreshTokenEvent event;
420 // This event concerns all accounts, so it does not have any account id.
421 event.type = AboutSigninInternals::RefreshTokenEventType::kAllTokensLoaded;
422 signin_status_.AddRefreshTokenEvent(event);
423 NotifyObservers();
424 }
425
OnEndBatchOfRefreshTokenStateChanges()426 void AboutSigninInternals::OnEndBatchOfRefreshTokenStateChanges() {
427 NotifyObservers();
428 }
429
OnAccessTokenRemovedFromCache(const CoreAccountId & account_id,const signin::ScopeSet & scopes)430 void AboutSigninInternals::OnAccessTokenRemovedFromCache(
431 const CoreAccountId& account_id,
432 const signin::ScopeSet& scopes) {
433 for (const std::unique_ptr<TokenInfo>& token :
434 signin_status_.token_info_map[account_id]) {
435 if (token->scopes == scopes)
436 token->Invalidate();
437 }
438 NotifyObservers();
439 }
440
OnRefreshTokenReceived(const std::string & status)441 void AboutSigninInternals::OnRefreshTokenReceived(const std::string& status) {
442 NotifyTimedSigninFieldValueChanged(
443 signin_internals_util::REFRESH_TOKEN_RECEIVED, status);
444 }
445
OnAuthenticationResultReceived(const std::string & status)446 void AboutSigninInternals::OnAuthenticationResultReceived(
447 const std::string& status) {
448 NotifyTimedSigninFieldValueChanged(
449 signin_internals_util::AUTHENTICATION_RESULT_RECEIVED, status);
450 }
451
OnErrorChanged()452 void AboutSigninInternals::OnErrorChanged() {
453 NotifyObservers();
454 }
455
OnBlockReconcile()456 void AboutSigninInternals::OnBlockReconcile() {
457 NotifyObservers();
458 }
459
OnUnblockReconcile()460 void AboutSigninInternals::OnUnblockReconcile() {
461 NotifyObservers();
462 }
463
OnPrimaryAccountSet(const CoreAccountInfo & primary_account_info)464 void AboutSigninInternals::OnPrimaryAccountSet(
465 const CoreAccountInfo& primary_account_info) {
466 NotifyObservers();
467 }
468
OnPrimaryAccountCleared(const CoreAccountInfo & primary_account_info)469 void AboutSigninInternals::OnPrimaryAccountCleared(
470 const CoreAccountInfo& primary_account_info) {
471 NotifyObservers();
472 }
473
OnAccountsInCookieUpdated(const signin::AccountsInCookieJarInfo & accounts_in_cookie_jar_info,const GoogleServiceAuthError & error)474 void AboutSigninInternals::OnAccountsInCookieUpdated(
475 const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
476 const GoogleServiceAuthError& error) {
477 if (error.state() != GoogleServiceAuthError::NONE)
478 return;
479
480 auto cookie_info = std::make_unique<base::ListValue>();
481
482 for (const auto& signed_in_account :
483 accounts_in_cookie_jar_info.signed_in_accounts) {
484 AddCookieEntry(cookie_info.get(), signed_in_account.raw_email,
485 signed_in_account.gaia_id,
486 signed_in_account.valid ? "Valid" : "Invalid");
487 }
488
489 if (accounts_in_cookie_jar_info.signed_in_accounts.size() == 0) {
490 AddCookieEntry(cookie_info.get(), "No Accounts Present.", std::string(),
491 std::string());
492 }
493
494 base::DictionaryValue cookie_status;
495 cookie_status.Set("cookie_info", std::move(cookie_info));
496 // Update the observers that the cookie's accounts are updated.
497 for (auto& observer : signin_observers_)
498 observer.OnCookieAccountsFetched(&cookie_status);
499 }
500
TokenInfo(const std::string & consumer_id,const signin::ScopeSet & scopes)501 AboutSigninInternals::TokenInfo::TokenInfo(const std::string& consumer_id,
502 const signin::ScopeSet& scopes)
503 : consumer_id(consumer_id),
504 scopes(scopes),
505 request_time(base::Time::Now()),
506 error(GoogleServiceAuthError::AuthErrorNone()),
507 removed_(false) {}
508
~TokenInfo()509 AboutSigninInternals::TokenInfo::~TokenInfo() {}
510
LessThan(const std::unique_ptr<TokenInfo> & a,const std::unique_ptr<TokenInfo> & b)511 bool AboutSigninInternals::TokenInfo::LessThan(
512 const std::unique_ptr<TokenInfo>& a,
513 const std::unique_ptr<TokenInfo>& b) {
514 return std::tie(a->request_time, a->consumer_id, a->scopes) <
515 std::tie(b->request_time, b->consumer_id, b->scopes);
516 }
517
Invalidate()518 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
519
520 std::unique_ptr<base::DictionaryValue>
ToValue() const521 AboutSigninInternals::TokenInfo::ToValue() const {
522 std::unique_ptr<base::DictionaryValue> token_info(
523 new base::DictionaryValue());
524 token_info->SetString("service", consumer_id);
525
526 std::string scopes_str;
527 for (auto it = scopes.begin(); it != scopes.end(); ++it) {
528 scopes_str += *it + "\n";
529 }
530 token_info->SetString("scopes", scopes_str);
531 token_info->SetString("request_time", base::TimeToISO8601(request_time));
532
533 if (removed_) {
534 token_info->SetString("status", "Token was revoked.");
535 } else if (!receive_time.is_null()) {
536 if (error == GoogleServiceAuthError::AuthErrorNone()) {
537 bool token_expired = expiration_time < base::Time::Now();
538 std::string expiration_time_string = base::TimeToISO8601(expiration_time);
539 if (expiration_time.is_null()) {
540 token_expired = false;
541 expiration_time_string = "Expiration time not available";
542 }
543 std::string status_str;
544 std::string expire_string = "Expire";
545 if (token_expired)
546 expire_string = "Expired";
547 base::StringAppendF(&status_str, "Received token at %s. %s at %s",
548 base::TimeToISO8601(receive_time).c_str(),
549 expire_string.c_str(),
550 expiration_time_string.c_str());
551 // JS code looks for `Expired at` string in order to mark
552 // specific status row red color. Changing `Exired at` status
553 // requires a change in JS code too.
554 token_info->SetString("status", status_str);
555 } else {
556 token_info->SetString(
557 "status",
558 base::StringPrintf("Failure: %s", error.ToString().c_str()));
559 }
560 } else {
561 token_info->SetString("status", "Waiting for response");
562 }
563
564 return token_info;
565 }
566
RefreshTokenEvent()567 AboutSigninInternals::RefreshTokenEvent::RefreshTokenEvent()
568 : timestamp(base::Time::Now()) {}
569
GetTypeAsString() const570 std::string AboutSigninInternals::RefreshTokenEvent::GetTypeAsString() const {
571 switch (type) {
572 case AboutSigninInternals::RefreshTokenEventType::kUpdateToRegular:
573 return "Updated";
574 case AboutSigninInternals::RefreshTokenEventType::kUpdateToInvalid:
575 return "Invalidated";
576 case AboutSigninInternals::RefreshTokenEventType::kRevokeRegular:
577 return "Revoked";
578 case AboutSigninInternals::RefreshTokenEventType::kAllTokensLoaded:
579 return "Loaded";
580 }
581 }
582
SigninStatus()583 AboutSigninInternals::SigninStatus::SigninStatus()
584 : timed_signin_fields(signin_internals_util::TIMED_FIELDS_COUNT) {}
585
~SigninStatus()586 AboutSigninInternals::SigninStatus::~SigninStatus() {}
587
FindToken(const CoreAccountId & account_id,const std::string & consumer_id,const signin::ScopeSet & scopes)588 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
589 const CoreAccountId& account_id,
590 const std::string& consumer_id,
591 const signin::ScopeSet& scopes) {
592 for (const std::unique_ptr<TokenInfo>& token : token_info_map[account_id]) {
593 if (token->consumer_id == consumer_id && token->scopes == scopes)
594 return token.get();
595 }
596 return nullptr;
597 }
598
AddRefreshTokenEvent(const AboutSigninInternals::RefreshTokenEvent & event)599 void AboutSigninInternals::SigninStatus::AddRefreshTokenEvent(
600 const AboutSigninInternals::RefreshTokenEvent& event) {
601 if (refresh_token_events.size() > kMaxRefreshTokenListSize)
602 refresh_token_events.pop_front();
603
604 refresh_token_events.push_back(event);
605 }
606
607 std::unique_ptr<base::DictionaryValue>
ToValue(signin::IdentityManager * identity_manager,SigninErrorController * signin_error_controller,SigninClient * signin_client,signin::AccountConsistencyMethod account_consistency,AccountReconcilor * account_reconcilor)608 AboutSigninInternals::SigninStatus::ToValue(
609 signin::IdentityManager* identity_manager,
610 SigninErrorController* signin_error_controller,
611 SigninClient* signin_client,
612 signin::AccountConsistencyMethod account_consistency,
613 AccountReconcilor* account_reconcilor) {
614 auto signin_status = std::make_unique<base::DictionaryValue>();
615 auto signin_info = std::make_unique<base::ListValue>();
616
617 // A summary of signin related info first.
618 base::ListValue* basic_info =
619 AddSection(signin_info.get(), "Basic Information");
620 AddSectionEntry(basic_info, "Account Consistency",
621 GetAccountConsistencyDescription(account_consistency));
622 AddSectionEntry(
623 basic_info, "Signin Status",
624 identity_manager->HasPrimaryAccount() ? "Signed In" : "Not Signed In");
625 signin::LoadCredentialsState load_tokens_state =
626 identity_manager->GetDiagnosticsProvider()
627 ->GetDetailedStateOfLoadingOfRefreshTokens();
628 AddSectionEntry(basic_info, "TokenService Load Status",
629 TokenServiceLoadCredentialsStateToLabel(load_tokens_state));
630 AddSectionEntry(
631 basic_info, "Gaia cookies state",
632 GetGaiaCookiesStateAsString(GetGaiaCookiesState(signin_client)));
633
634 if (identity_manager->HasPrimaryAccount()) {
635 CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo();
636 AddSectionEntry(basic_info,
637 SigninStatusFieldToLabel(signin_internals_util::ACCOUNT_ID),
638 account_info.account_id.ToString());
639 AddSectionEntry(basic_info,
640 SigninStatusFieldToLabel(signin_internals_util::GAIA_ID),
641 account_info.gaia);
642 AddSectionEntry(basic_info,
643 SigninStatusFieldToLabel(signin_internals_util::USERNAME),
644 account_info.email);
645 if (signin_error_controller->HasError()) {
646 const CoreAccountId error_account_id =
647 signin_error_controller->error_account_id();
648 const base::Optional<AccountInfo> error_account_info =
649 identity_manager
650 ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
651 error_account_id);
652 AddSectionEntry(basic_info, "Auth Error",
653 signin_error_controller->auth_error().ToString());
654 AddSectionEntry(basic_info, "Auth Error Account Id",
655 error_account_id.ToString());
656
657 // The error_account_info optional should never be unset when we reach
658 // this line (as we should have a refresh token, even if in an error
659 // state). However, since this is a debug page, make the code resilient
660 // to avoid rendering the page unavailable to debug if a regression is
661 // introduced (and thus making debugging the regression harder).
662 AddSectionEntry(basic_info, "Auth Error Username",
663 error_account_info ? error_account_info->email : "");
664 } else {
665 AddSectionEntry(basic_info, "Auth Error", "None");
666 }
667 }
668
669 AddSectionEntry(basic_info, "Account Reconcilor blocked",
670 account_reconcilor->IsReconcileBlocked() ? "True" : "False");
671
672 #if !defined(OS_CHROMEOS)
673 // Time and status information of the possible sign in types.
674 base::ListValue* detailed_info =
675 AddSection(signin_info.get(), "Last Signin Details");
676 for (signin_internals_util::TimedSigninStatusField i =
677 signin_internals_util::TIMED_FIELDS_BEGIN;
678 i < signin_internals_util::TIMED_FIELDS_END; ++i) {
679 const std::string status_field_label = SigninStatusFieldToLabel(i);
680
681 AddSectionEntry(
682 detailed_info, status_field_label,
683 timed_signin_fields[i - signin_internals_util::TIMED_FIELDS_BEGIN]
684 .first,
685 timed_signin_fields[i - signin_internals_util::TIMED_FIELDS_BEGIN]
686 .second);
687 }
688
689 base::TimeDelta cookie_requests_delay =
690 identity_manager->GetDiagnosticsProvider()
691 ->GetDelayBeforeMakingCookieRequests();
692
693 if (cookie_requests_delay > base::TimeDelta()) {
694 base::Time next_retry_time =
695 base::Time::NowFromSystemTime() + cookie_requests_delay;
696 AddSectionEntry(detailed_info, "Cookie Manager Next Retry",
697 base::TimeToISO8601(next_retry_time), "");
698 }
699
700 base::TimeDelta token_requests_delay =
701 identity_manager->GetDiagnosticsProvider()
702 ->GetDelayBeforeMakingAccessTokenRequests();
703
704 if (token_requests_delay > base::TimeDelta()) {
705 base::Time next_retry_time =
706 base::Time::NowFromSystemTime() + token_requests_delay;
707 AddSectionEntry(detailed_info, "Token Service Next Retry",
708 base::TimeToISO8601(next_retry_time), "");
709 }
710 #endif // !defined(OS_CHROMEOS)
711 signin_status->Set("signin_info", std::move(signin_info));
712
713 // Token information for all services.
714 auto token_info = std::make_unique<base::ListValue>();
715 for (auto it = token_info_map.begin(); it != token_info_map.end(); ++it) {
716 base::ListValue* token_details =
717 AddSection(token_info.get(), it->first.ToString());
718 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
719 for (const std::unique_ptr<TokenInfo>& token : it->second)
720 token_details->Append(token->ToValue());
721 }
722 signin_status->Set("token_info", std::move(token_info));
723
724 // Account info section
725 auto account_info_section = std::make_unique<base::ListValue>();
726 const std::vector<CoreAccountInfo>& accounts_with_refresh_tokens =
727 identity_manager->GetAccountsWithRefreshTokens();
728 if (accounts_with_refresh_tokens.size() == 0) {
729 auto no_token_entry = std::make_unique<base::DictionaryValue>();
730 no_token_entry->SetString("accountId", "No token in Token Service.");
731 account_info_section->Append(std::move(no_token_entry));
732 } else {
733 for (const CoreAccountInfo& account_info : accounts_with_refresh_tokens) {
734 auto entry = std::make_unique<base::DictionaryValue>();
735 entry->SetString("accountId", account_info.account_id.ToString());
736 // TODO(https://crbug.com/919793): Remove this field once the token
737 // service is internally consistent on all platforms.
738 entry->SetBoolean("hasRefreshToken",
739 identity_manager->HasAccountWithRefreshToken(
740 account_info.account_id));
741 entry->SetBoolean(
742 "hasAuthError",
743 identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
744 account_info.account_id));
745 account_info_section->Append(std::move(entry));
746 }
747 }
748 signin_status->Set("accountInfo", std::move(account_info_section));
749
750 // Refresh token events section
751 auto refresh_token_events_value = std::make_unique<base::ListValue>();
752 for (const auto& event : refresh_token_events) {
753 auto entry = std::make_unique<base::DictionaryValue>();
754 entry->SetString("accountId", event.account_id.ToString());
755 entry->SetString("timestamp", base::TimeToISO8601(event.timestamp));
756 entry->SetString("type", event.GetTypeAsString());
757 entry->SetString("source", event.source);
758 refresh_token_events_value->Append(std::move(entry));
759 }
760 signin_status->Set("refreshTokenEvents",
761 std::move(refresh_token_events_value));
762
763 return signin_status;
764 }
765