ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / components / signin / core / browser / about_signin_internals.cc
blob6008d05b67170eaf70229ac72d38e9f5c6ec85c3
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.
5 #include "components/signin/core/browser/about_signin_internals.h"
7 #include "base/command_line.h"
8 #include "base/hash.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/trace_event/trace_event.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_auth_fetcher.h"
22 #include "google_apis/gaia/gaia_auth_util.h"
23 #include "google_apis/gaia/gaia_constants.h"
24 #include "google_apis/gaia/gaia_urls.h"
25 #include "net/cookies/canonical_cookie.h"
27 using base::Time;
28 using namespace signin_internals_util;
30 namespace {
32 std::string GetTimeStr(base::Time time) {
33 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
36 base::ListValue* AddSection(base::ListValue* parent_list,
37 const std::string& title) {
38 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
39 base::ListValue* section_contents = new base::ListValue();
41 section->SetString("title", title);
42 section->Set("data", section_contents);
43 parent_list->Append(section.release());
44 return section_contents;
47 void AddSectionEntry(base::ListValue* section_list,
48 const std::string& field_name,
49 const std::string& field_status,
50 const std::string& field_time = "") {
51 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
52 entry->SetString("label", field_name);
53 entry->SetString("status", field_status);
54 entry->SetString("time", field_time);
55 section_list->Append(entry.release());
58 void AddCookieEntry(base::ListValue* accounts_list,
59 const std::string& field_email,
60 const std::string& field_valid) {
61 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
62 entry->SetString("email", field_email);
63 entry->SetString("valid", field_valid);
64 accounts_list->Append(entry.release());
67 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
68 switch (field) {
69 case USERNAME:
70 return "User Id";
71 case UNTIMED_FIELDS_END:
72 NOTREACHED();
73 return std::string();
75 NOTREACHED();
76 return std::string();
79 #if !defined (OS_CHROMEOS)
80 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
81 switch (field) {
82 case SIGNIN_TYPE:
83 return "Type";
84 case AUTHENTICATION_RESULT_RECEIVED:
85 return "Last Authentication Result Received";
86 case REFRESH_TOKEN_RECEIVED:
87 return "Last RefreshToken Received";
88 case GET_USER_INFO_STATUS:
89 return "Last OnGetUserInfo Received";
90 case UBER_TOKEN_STATUS:
91 return "Last OnUberToken Received";
92 case MERGE_SESSION_STATUS:
93 return "Last OnMergeSession Received";
94 case TIMED_FIELDS_END:
95 NOTREACHED();
96 return "Error";
98 NOTREACHED();
99 return "Error";
101 #endif // !defined (OS_CHROMEOS)
103 } // anonymous namespace
105 AboutSigninInternals::AboutSigninInternals(
106 ProfileOAuth2TokenService* token_service,
107 SigninManagerBase* signin_manager)
108 : token_service_(token_service),
109 signin_manager_(signin_manager),
110 client_(NULL) {}
112 AboutSigninInternals::~AboutSigninInternals() {}
114 void AboutSigninInternals::AddSigninObserver(
115 AboutSigninInternals::Observer* observer) {
116 signin_observers_.AddObserver(observer);
119 void AboutSigninInternals::RemoveSigninObserver(
120 AboutSigninInternals::Observer* observer) {
121 signin_observers_.RemoveObserver(observer);
124 void AboutSigninInternals::NotifySigninValueChanged(
125 const UntimedSigninStatusField& field,
126 const std::string& value) {
127 unsigned int field_index = field - UNTIMED_FIELDS_BEGIN;
128 DCHECK(field_index >= 0 &&
129 field_index < signin_status_.untimed_signin_fields.size());
131 signin_status_.untimed_signin_fields[field_index] = value;
133 // Also persist these values in the prefs.
134 const std::string pref_path = SigninStatusFieldToString(field);
135 client_->GetPrefs()->SetString(pref_path.c_str(), value);
137 NotifyObservers();
140 void AboutSigninInternals::NotifySigninValueChanged(
141 const TimedSigninStatusField& field,
142 const std::string& value) {
143 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
144 DCHECK(field_index >= 0 &&
145 field_index < signin_status_.timed_signin_fields.size());
147 Time now = Time::NowFromSystemTime();
148 std::string time_as_str =
149 base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now));
150 TimedSigninStatusValue timed_value(value, time_as_str);
152 signin_status_.timed_signin_fields[field_index] = timed_value;
154 // Also persist these values in the prefs.
155 const std::string value_pref = SigninStatusFieldToString(field) + ".value";
156 const std::string time_pref = SigninStatusFieldToString(field) + ".time";
157 client_->GetPrefs()->SetString(value_pref.c_str(), value);
158 client_->GetPrefs()->SetString(time_pref.c_str(), time_as_str);
160 NotifyObservers();
163 void AboutSigninInternals::RefreshSigninPrefs() {
164 // Since the AboutSigninInternals has a dependency on the SigninManager
165 // (as seen in the AboutSigninInternalsFactory) the SigninManager can have
166 // the AuthenticatedUsername set before AboutSigninInternals can observe it.
167 // For that scenario, read the AuthenticatedUsername if it exists.
168 if (signin_manager_->IsAuthenticated()) {
169 signin_status_.untimed_signin_fields[USERNAME] =
170 signin_manager_->GetAuthenticatedUsername();
173 // Return if no client exists. Can occur in unit tests.
174 if (!client_)
175 return;
177 PrefService* pref_service = client_->GetPrefs();
178 for (int i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
179 const std::string pref_path =
180 SigninStatusFieldToString(static_cast<UntimedSigninStatusField>(i));
182 signin_status_.untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN] =
183 pref_service->GetString(pref_path.c_str());
185 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
186 const std::string value_pref =
187 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
188 ".value";
189 const std::string time_pref =
190 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
191 ".time";
193 TimedSigninStatusValue value(pref_service->GetString(value_pref.c_str()),
194 pref_service->GetString(time_pref.c_str()));
195 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
198 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
200 NotifyObservers();
203 void AboutSigninInternals::Initialize(SigninClient* client) {
204 DCHECK(!client_);
205 client_ = client;
207 RefreshSigninPrefs();
209 signin_manager_->AddSigninDiagnosticsObserver(this);
210 token_service_->AddDiagnosticsObserver(this);
211 cookie_changed_subscription_ = client_->AddCookieChangedCallback(
212 GaiaUrls::GetInstance()->gaia_url(),
213 "LSID",
214 base::Bind(&AboutSigninInternals::OnCookieChanged,
215 base::Unretained(this)));
218 void AboutSigninInternals::Shutdown() {
219 signin_manager_->RemoveSigninDiagnosticsObserver(this);
220 token_service_->RemoveDiagnosticsObserver(this);
221 cookie_changed_subscription_.reset();
224 void AboutSigninInternals::NotifyObservers() {
225 if (!signin_observers_.might_have_observers())
226 return;
228 const std::string product_version = client_->GetProductVersion();
229 scoped_ptr<base::DictionaryValue> signin_status_value =
230 signin_status_.ToValue(product_version);
232 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
233 signin_observers_,
234 OnSigninStateChanged(signin_status_value.get()));
237 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
238 return signin_status_.ToValue(client_->GetProductVersion()).Pass();
241 void AboutSigninInternals::OnAccessTokenRequested(
242 const std::string& account_id,
243 const std::string& consumer_id,
244 const OAuth2TokenService::ScopeSet& scopes) {
245 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
246 if (token) {
247 *token = TokenInfo(consumer_id, scopes);
248 } else {
249 token = new TokenInfo(consumer_id, scopes);
250 signin_status_.token_info_map[account_id].push_back(token);
253 NotifyObservers();
256 void AboutSigninInternals::OnFetchAccessTokenComplete(
257 const std::string& account_id,
258 const std::string& consumer_id,
259 const OAuth2TokenService::ScopeSet& scopes,
260 GoogleServiceAuthError error,
261 base::Time expiration_time) {
262 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
263 if (!token) {
264 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
265 return;
268 token->receive_time = base::Time::Now();
269 token->error = error;
270 token->expiration_time = expiration_time;
272 NotifyObservers();
275 void AboutSigninInternals::OnTokenRemoved(
276 const std::string& account_id,
277 const OAuth2TokenService::ScopeSet& scopes) {
278 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
279 ++i) {
280 TokenInfo* token = signin_status_.token_info_map[account_id][i];
281 if (token->scopes == scopes)
282 token->Invalidate();
284 NotifyObservers();
287 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
288 NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
291 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
292 NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
295 void AboutSigninInternals::OnCookieChanged(const net::CanonicalCookie& cookie,
296 bool removed) {
297 DCHECK_EQ("LSID", cookie.Name());
298 DCHECK_EQ(GaiaUrls::GetInstance()->gaia_url().host(), cookie.Domain());
299 if (cookie.IsSecure() && cookie.IsHttpOnly()) {
300 GetCookieAccountsAsync();
304 void AboutSigninInternals::GetCookieAccountsAsync() {
305 // Don't bother calling /ListAccounts if no one will observe the response.
306 if (!gaia_fetcher_ && signin_observers_.might_have_observers()) {
307 // There is no list account request in flight.
308 gaia_fetcher_.reset(new GaiaAuthFetcher(
309 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext()));
310 gaia_fetcher_->StartListAccounts();
314 void AboutSigninInternals::OnListAccountsSuccess(const std::string& data) {
315 gaia_fetcher_.reset();
317 // Get account information from response data.
318 std::vector<std::pair<std::string, bool> > gaia_accounts;
319 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
320 if (!valid_json) {
321 VLOG(1) << "AboutSigninInternals::OnListAccountsSuccess: parsing error";
322 } else {
323 OnListAccountsComplete(gaia_accounts);
327 void AboutSigninInternals::OnListAccountsFailure(
328 const GoogleServiceAuthError& error) {
329 gaia_fetcher_.reset();
330 VLOG(1) << "AboutSigninInternals::OnListAccountsFailure:" << error.ToString();
333 void AboutSigninInternals::OnListAccountsComplete(
334 std::vector<std::pair<std::string, bool> >& gaia_accounts) {
335 base::DictionaryValue signin_status;
336 base::ListValue* cookie_info = new base::ListValue();
337 signin_status.Set("cookie_info", cookie_info);
339 for (size_t i = 0; i < gaia_accounts.size(); ++i) {
340 AddCookieEntry(cookie_info,
341 gaia_accounts[i].first,
342 gaia_accounts[i].second ? "Valid" : "Invalid");
345 if (gaia_accounts.size() == 0)
346 AddCookieEntry(cookie_info, "No Accounts Present.", "");
348 // Update the observers that the cookie's accounts are updated.
349 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
350 signin_observers_,
351 OnCookieAccountsFetched(&signin_status));
354 AboutSigninInternals::TokenInfo::TokenInfo(
355 const std::string& consumer_id,
356 const OAuth2TokenService::ScopeSet& scopes)
357 : consumer_id(consumer_id),
358 scopes(scopes),
359 request_time(base::Time::Now()),
360 error(GoogleServiceAuthError::AuthErrorNone()),
361 removed_(false) {}
363 AboutSigninInternals::TokenInfo::~TokenInfo() {}
365 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
366 const TokenInfo* b) {
367 return a->consumer_id < b->consumer_id ||
368 (a->consumer_id == b->consumer_id && a->scopes < b->scopes);
371 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
373 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
374 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
375 token_info->SetString("service", consumer_id);
377 std::string scopes_str;
378 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
379 it != scopes.end();
380 ++it) {
381 scopes_str += *it + "<br/>";
383 token_info->SetString("scopes", scopes_str);
384 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
386 if (removed_) {
387 token_info->SetString("status", "Token was revoked.");
388 } else if (!receive_time.is_null()) {
389 if (error == GoogleServiceAuthError::AuthErrorNone()) {
390 bool token_expired = expiration_time < base::Time::Now();
391 std::string status_str = "";
392 if (token_expired)
393 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
394 base::StringAppendF(&status_str,
395 "Received token at %s. Expire at %s",
396 GetTimeStr(receive_time).c_str(),
397 GetTimeStr(expiration_time).c_str());
398 if (token_expired)
399 base::StringAppendF(&status_str, "</p>");
400 token_info->SetString("status", status_str);
401 } else {
402 token_info->SetString(
403 "status",
404 base::StringPrintf("Failure: %s", error.ToString().c_str()));
406 } else {
407 token_info->SetString("status", "Waiting for response");
410 return token_info.release();
413 AboutSigninInternals::SigninStatus::SigninStatus()
414 : untimed_signin_fields(UNTIMED_FIELDS_COUNT),
415 timed_signin_fields(TIMED_FIELDS_COUNT) {}
417 AboutSigninInternals::SigninStatus::~SigninStatus() {
418 for (TokenInfoMap::iterator it = token_info_map.begin();
419 it != token_info_map.end();
420 ++it) {
421 STLDeleteElements(&it->second);
425 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
426 const std::string& account_id,
427 const std::string& consumer_id,
428 const OAuth2TokenService::ScopeSet& scopes) {
429 for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
430 TokenInfo* tmp = token_info_map[account_id][i];
431 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
432 return tmp;
434 return NULL;
437 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
438 std::string product_version) {
439 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
440 base::ListValue* signin_info = new base::ListValue();
441 signin_status->Set("signin_info", signin_info);
443 // A summary of signin related info first.
444 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
445 const std::string signin_status_string =
446 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty()
447 ? "Not Signed In"
448 : "Signed In";
449 AddSectionEntry(basic_info, "Chrome Version", product_version);
450 AddSectionEntry(basic_info, "Signin Status", signin_status_string);
451 AddSectionEntry(basic_info, "Webview Based Signin Enabled?",
452 switches::IsEnableWebviewBasedSignin() == true ? "True" : "False");
453 AddSectionEntry(basic_info, "New Avatar Menu Enabled?",
454 switches::IsNewAvatarMenu() == true ? "True" : "False");
455 AddSectionEntry(basic_info, "New Profile Management Enabled?",
456 switches::IsNewProfileManagement() == true ? "True" : "False");
457 AddSectionEntry(basic_info, "Account Consistency Enabled?",
458 switches::IsEnableAccountConsistency() == true ? "True" : "False");
460 // Only add username. SID and LSID have moved to tokens section.
461 const std::string field =
462 SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
463 AddSectionEntry(basic_info,
464 field,
465 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
467 #if !defined(OS_CHROMEOS)
468 // Time and status information of the possible sign in types.
469 base::ListValue* detailed_info =
470 AddSection(signin_info, "Last Signin Details");
471 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
472 const std::string status_field_label =
473 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
475 AddSectionEntry(detailed_info,
476 status_field_label,
477 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
478 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
480 #endif // !defined(OS_CHROMEOS)
482 // Token information for all services.
483 base::ListValue* token_info = new base::ListValue();
484 signin_status->Set("token_info", token_info);
485 for (TokenInfoMap::iterator it = token_info_map.begin();
486 it != token_info_map.end();
487 ++it) {
488 base::ListValue* token_details = AddSection(token_info, it->first);
489 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
490 const std::vector<TokenInfo*>& tokens = it->second;
492 for (size_t i = 0; i < tokens.size(); ++i) {
493 base::DictionaryValue* token_info = tokens[i]->ToValue();
494 token_details->Append(token_info);
498 return signin_status.Pass();