• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/command_line.h"
8 #include "base/debug/trace_event.h"
9 #include "base/hash.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/logging.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "components/signin/core/browser/profile_oauth2_token_service.h"
16 #include "components/signin/core/browser/signin_client.h"
17 #include "components/signin/core/browser/signin_internals_util.h"
18 #include "components/signin/core/browser/signin_manager.h"
19 #include "components/signin/core/common/profile_management_switches.h"
20 #include "components/signin/core/common/signin_switches.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 
23 using base::Time;
24 using namespace signin_internals_util;
25 
26 namespace {
27 
GetTimeStr(base::Time time)28 std::string GetTimeStr(base::Time time) {
29   return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
30 }
31 
AddSection(base::ListValue * parent_list,const std::string & title)32 base::ListValue* AddSection(base::ListValue* parent_list,
33                             const std::string& title) {
34   scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
35   base::ListValue* section_contents = new base::ListValue();
36 
37   section->SetString("title", title);
38   section->Set("data", section_contents);
39   parent_list->Append(section.release());
40   return section_contents;
41 }
42 
AddSectionEntry(base::ListValue * section_list,const std::string & field_name,const std::string & field_status,const std::string & field_time="")43 void AddSectionEntry(base::ListValue* section_list,
44                      const std::string& field_name,
45                      const std::string& field_status,
46                      const std::string& field_time = "") {
47   scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
48   entry->SetString("label", field_name);
49   entry->SetString("status", field_status);
50   entry->SetString("time", field_time);
51   section_list->Append(entry.release());
52 }
53 
SigninStatusFieldToLabel(UntimedSigninStatusField field)54 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
55   switch (field) {
56     case USERNAME:
57       return "User Id";
58     case UNTIMED_FIELDS_END:
59       NOTREACHED();
60       return std::string();
61   }
62   NOTREACHED();
63   return std::string();
64 }
65 
SigninStatusFieldToLabel(TimedSigninStatusField field)66 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
67   switch (field) {
68     case SIGNIN_TYPE:
69       return "Type";
70     case AUTHENTICATION_RESULT_RECEIVED:
71       return "Last Authentication Result Received";
72     case REFRESH_TOKEN_RECEIVED:
73       return "Last RefreshToken Received";
74     case GET_USER_INFO_STATUS:
75       return "Last OnGetUserInfo Received";
76     case UBER_TOKEN_STATUS:
77       return "Last OnUberToken Received";
78     case MERGE_SESSION_STATUS:
79       return "Last OnMergeSession Received";
80     case TIMED_FIELDS_END:
81       NOTREACHED();
82       return "Error";
83   }
84   NOTREACHED();
85   return "Error";
86 }
87 
88 }  // anonymous namespace
89 
AboutSigninInternals(ProfileOAuth2TokenService * token_service,SigninManagerBase * signin_manager)90 AboutSigninInternals::AboutSigninInternals(
91     ProfileOAuth2TokenService* token_service,
92     SigninManagerBase* signin_manager)
93     : token_service_(token_service),
94       signin_manager_(signin_manager),
95       client_(NULL) {}
96 
~AboutSigninInternals()97 AboutSigninInternals::~AboutSigninInternals() {}
98 
AddSigninObserver(AboutSigninInternals::Observer * observer)99 void AboutSigninInternals::AddSigninObserver(
100     AboutSigninInternals::Observer* observer) {
101   signin_observers_.AddObserver(observer);
102 }
103 
RemoveSigninObserver(AboutSigninInternals::Observer * observer)104 void AboutSigninInternals::RemoveSigninObserver(
105     AboutSigninInternals::Observer* observer) {
106   signin_observers_.RemoveObserver(observer);
107 }
108 
NotifySigninValueChanged(const UntimedSigninStatusField & field,const std::string & value)109 void AboutSigninInternals::NotifySigninValueChanged(
110     const UntimedSigninStatusField& field,
111     const std::string& value) {
112   unsigned int field_index = field - UNTIMED_FIELDS_BEGIN;
113   DCHECK(field_index >= 0 &&
114          field_index < signin_status_.untimed_signin_fields.size());
115 
116   signin_status_.untimed_signin_fields[field_index] = value;
117 
118   // Also persist these values in the prefs.
119   const std::string pref_path = SigninStatusFieldToString(field);
120   client_->GetPrefs()->SetString(pref_path.c_str(), value);
121 
122   NotifyObservers();
123 }
124 
NotifySigninValueChanged(const TimedSigninStatusField & field,const std::string & value)125 void AboutSigninInternals::NotifySigninValueChanged(
126     const TimedSigninStatusField& field,
127     const std::string& value) {
128   unsigned int field_index = field - TIMED_FIELDS_BEGIN;
129   DCHECK(field_index >= 0 &&
130          field_index < signin_status_.timed_signin_fields.size());
131 
132   Time now = Time::NowFromSystemTime();
133   std::string time_as_str =
134       base::UTF16ToUTF8(base::TimeFormatFriendlyDate(now));
135   TimedSigninStatusValue timed_value(value, time_as_str);
136 
137   signin_status_.timed_signin_fields[field_index] = timed_value;
138 
139   // Also persist these values in the prefs.
140   const std::string value_pref = SigninStatusFieldToString(field) + ".value";
141   const std::string time_pref = SigninStatusFieldToString(field) + ".time";
142   client_->GetPrefs()->SetString(value_pref.c_str(), value);
143   client_->GetPrefs()->SetString(time_pref.c_str(), time_as_str);
144 
145   NotifyObservers();
146 }
147 
RefreshSigninPrefs()148 void AboutSigninInternals::RefreshSigninPrefs() {
149   // Return if no client exists. Can occur in unit tests.
150   if (!client_)
151     return;
152 
153   PrefService* pref_service = client_->GetPrefs();
154   for (int i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
155     const std::string pref_path =
156         SigninStatusFieldToString(static_cast<UntimedSigninStatusField>(i));
157 
158     signin_status_.untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN] =
159         pref_service->GetString(pref_path.c_str());
160   }
161   for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
162     const std::string value_pref =
163         SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
164         ".value";
165     const std::string time_pref =
166         SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
167         ".time";
168 
169     TimedSigninStatusValue value(pref_service->GetString(value_pref.c_str()),
170                                  pref_service->GetString(time_pref.c_str()));
171     signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
172   }
173 
174   // TODO(rogerta): Get status and timestamps for oauth2 tokens.
175 
176   NotifyObservers();
177 }
178 
Initialize(SigninClient * client)179 void AboutSigninInternals::Initialize(SigninClient* client) {
180   DCHECK(!client_);
181   client_ = client;
182 
183   RefreshSigninPrefs();
184 
185   signin_manager_->AddSigninDiagnosticsObserver(this);
186   token_service_->AddDiagnosticsObserver(this);
187 }
188 
Shutdown()189 void AboutSigninInternals::Shutdown() {
190   signin_manager_->RemoveSigninDiagnosticsObserver(this);
191   token_service_->RemoveDiagnosticsObserver(this);
192 }
193 
NotifyObservers()194 void AboutSigninInternals::NotifyObservers() {
195   FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
196                     signin_observers_,
197                     OnSigninStateChanged(
198                         signin_status_.ToValue(client_->GetProductVersion())));
199 }
200 
GetSigninStatus()201 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
202   return signin_status_.ToValue(client_->GetProductVersion()).Pass();
203 }
204 
OnAccessTokenRequested(const std::string & account_id,const std::string & consumer_id,const OAuth2TokenService::ScopeSet & scopes)205 void AboutSigninInternals::OnAccessTokenRequested(
206     const std::string& account_id,
207     const std::string& consumer_id,
208     const OAuth2TokenService::ScopeSet& scopes) {
209   TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
210   if (token) {
211     *token = TokenInfo(consumer_id, scopes);
212   } else {
213     token = new TokenInfo(consumer_id, scopes);
214     signin_status_.token_info_map[account_id].push_back(token);
215   }
216 
217   NotifyObservers();
218 }
219 
OnFetchAccessTokenComplete(const std::string & account_id,const std::string & consumer_id,const OAuth2TokenService::ScopeSet & scopes,GoogleServiceAuthError error,base::Time expiration_time)220 void AboutSigninInternals::OnFetchAccessTokenComplete(
221     const std::string& account_id,
222     const std::string& consumer_id,
223     const OAuth2TokenService::ScopeSet& scopes,
224     GoogleServiceAuthError error,
225     base::Time expiration_time) {
226   TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
227   if (!token) {
228     DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
229     return;
230   }
231 
232   token->receive_time = base::Time::Now();
233   token->error = error;
234   token->expiration_time = expiration_time;
235 
236   NotifyObservers();
237 }
238 
OnTokenRemoved(const std::string & account_id,const OAuth2TokenService::ScopeSet & scopes)239 void AboutSigninInternals::OnTokenRemoved(
240     const std::string& account_id,
241     const OAuth2TokenService::ScopeSet& scopes) {
242   for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
243        ++i) {
244     TokenInfo* token = signin_status_.token_info_map[account_id][i];
245     if (token->scopes == scopes)
246       token->Invalidate();
247   }
248   NotifyObservers();
249 }
250 
OnRefreshTokenReceived(std::string status)251 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
252   NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
253 }
254 
OnAuthenticationResultReceived(std::string status)255 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
256   NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
257 }
258 
TokenInfo(const std::string & consumer_id,const OAuth2TokenService::ScopeSet & scopes)259 AboutSigninInternals::TokenInfo::TokenInfo(
260     const std::string& consumer_id,
261     const OAuth2TokenService::ScopeSet& scopes)
262     : consumer_id(consumer_id),
263       scopes(scopes),
264       request_time(base::Time::Now()),
265       error(GoogleServiceAuthError::AuthErrorNone()),
266       removed_(false) {}
267 
~TokenInfo()268 AboutSigninInternals::TokenInfo::~TokenInfo() {}
269 
LessThan(const TokenInfo * a,const TokenInfo * b)270 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
271                                                const TokenInfo* b) {
272   return a->consumer_id < b->consumer_id || a->scopes < b->scopes;
273 }
274 
Invalidate()275 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
276 
ToValue() const277 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
278   scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
279   token_info->SetString("service", consumer_id);
280 
281   std::string scopes_str;
282   for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
283        it != scopes.end();
284        ++it) {
285     scopes_str += *it + "<br/>";
286   }
287   token_info->SetString("scopes", scopes_str);
288   token_info->SetString("request_time", GetTimeStr(request_time).c_str());
289 
290   if (removed_) {
291     token_info->SetString("status", "Token was revoked.");
292   } else if (!receive_time.is_null()) {
293     if (error == GoogleServiceAuthError::AuthErrorNone()) {
294       bool token_expired = expiration_time < base::Time::Now();
295       std::string status_str = "";
296       if (token_expired)
297         status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
298       base::StringAppendF(&status_str,
299                           "Received token at %s. Expire at %s",
300                           GetTimeStr(receive_time).c_str(),
301                           GetTimeStr(expiration_time).c_str());
302       if (token_expired)
303         base::StringAppendF(&status_str, "</p>");
304       token_info->SetString("status", status_str);
305     } else {
306       token_info->SetString(
307           "status",
308           base::StringPrintf("Failure: %s", error.ToString().c_str()));
309     }
310   } else {
311     token_info->SetString("status", "Waiting for response");
312   }
313 
314   return token_info.release();
315 }
316 
SigninStatus()317 AboutSigninInternals::SigninStatus::SigninStatus()
318     : untimed_signin_fields(UNTIMED_FIELDS_COUNT),
319       timed_signin_fields(TIMED_FIELDS_COUNT) {}
320 
~SigninStatus()321 AboutSigninInternals::SigninStatus::~SigninStatus() {
322   for (TokenInfoMap::iterator it = token_info_map.begin();
323        it != token_info_map.end();
324        ++it) {
325     STLDeleteElements(&it->second);
326   }
327 }
328 
FindToken(const std::string & account_id,const std::string & consumer_id,const OAuth2TokenService::ScopeSet & scopes)329 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
330     const std::string& account_id,
331     const std::string& consumer_id,
332     const OAuth2TokenService::ScopeSet& scopes) {
333   for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
334     TokenInfo* tmp = token_info_map[account_id][i];
335     if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
336       return tmp;
337   }
338   return NULL;
339 }
340 
ToValue(std::string product_version)341 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
342     std::string product_version) {
343   scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
344   base::ListValue* signin_info = new base::ListValue();
345   signin_status->Set("signin_info", signin_info);
346 
347   // A summary of signin related info first.
348   base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
349   const std::string signin_status_string =
350       untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty()
351           ? "Not Signed In"
352           : "Signed In";
353   AddSectionEntry(basic_info, "Chrome Version", product_version);
354   AddSectionEntry(basic_info, "Signin Status", signin_status_string);
355   AddSectionEntry(basic_info, "Web Based Signin Enabled?",
356       switches::IsEnableWebBasedSignin() == true ? "True" : "False");
357   AddSectionEntry(basic_info, "New Profile Management Enabled?",
358       switches::IsNewProfileManagement() == true ? "True" : "False");
359   AddSectionEntry(basic_info, "New Avatar Menu Enabled?",
360       switches::IsNewAvatarMenu() == true ? "True" : "False");
361   bool new_avatar_menu_flag =
362       CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewAvatarMenu);
363   AddSectionEntry(basic_info, "New Avatar Menu Flag Set?",
364       new_avatar_menu_flag ? "True" : "False");
365   AddSectionEntry(basic_info, "Account Consistency Enabled?",
366       switches::IsEnableAccountConsistency() == true ? "True" : "False");
367 
368   // Only add username.  SID and LSID have moved to tokens section.
369   const std::string field =
370       SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
371   AddSectionEntry(basic_info,
372                   field,
373                   untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
374 
375   // Time and status information of the possible sign in types.
376   base::ListValue* detailed_info =
377       AddSection(signin_info, "Last Signin Details");
378   for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
379     const std::string status_field_label =
380         SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
381 
382     AddSectionEntry(detailed_info,
383                     status_field_label,
384                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
385                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
386   }
387 
388   // Token information for all services.
389   base::ListValue* token_info = new base::ListValue();
390   signin_status->Set("token_info", token_info);
391   for (TokenInfoMap::iterator it = token_info_map.begin();
392        it != token_info_map.end();
393        ++it) {
394     base::ListValue* token_details = AddSection(token_info, it->first);
395 
396     std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
397     const std::vector<TokenInfo*>& tokens = it->second;
398     for (size_t i = 0; i < tokens.size(); ++i) {
399       base::DictionaryValue* token_info = tokens[i]->ToValue();
400       token_details->Append(token_info);
401     }
402   }
403 
404   return signin_status.Pass();
405 }
406