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