Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / components / signin / core / browser / about_signin_internals.cc
blobe88b0f0d7e6610707d5564df528a3ae0d75e2457
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/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/profiler/scoped_tracker.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/signin/core/browser/profile_oauth2_token_service.h"
17 #include "components/signin/core/browser/signin_client.h"
18 #include "components/signin/core/browser/signin_internals_util.h"
19 #include "components/signin/core/browser/signin_manager.h"
20 #include "components/signin/core/common/profile_management_switches.h"
21 #include "components/signin/core/common/signin_switches.h"
22 #include "google_apis/gaia/gaia_auth_fetcher.h"
23 #include "google_apis/gaia/gaia_auth_util.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "google_apis/gaia/gaia_urls.h"
26 #include "net/cookies/canonical_cookie.h"
28 using base::Time;
29 using namespace signin_internals_util;
31 namespace {
33 std::string GetTimeStr(base::Time time) {
34 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
37 base::ListValue* AddSection(base::ListValue* parent_list,
38 const std::string& title) {
39 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
40 base::ListValue* section_contents = new base::ListValue();
42 section->SetString("title", title);
43 section->Set("data", section_contents);
44 parent_list->Append(section.release());
45 return section_contents;
48 void AddSectionEntry(base::ListValue* section_list,
49 const std::string& field_name,
50 const std::string& field_status,
51 const std::string& field_time = "") {
52 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
53 entry->SetString("label", field_name);
54 entry->SetString("status", field_status);
55 entry->SetString("time", field_time);
56 section_list->Append(entry.release());
59 void AddCookieEntry(base::ListValue* accounts_list,
60 const std::string& field_email,
61 const std::string& field_valid) {
62 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
63 entry->SetString("email", field_email);
64 entry->SetString("valid", field_valid);
65 accounts_list->Append(entry.release());
68 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
69 switch (field) {
70 case USERNAME:
71 return "User Id";
72 case UNTIMED_FIELDS_END:
73 NOTREACHED();
74 return std::string();
76 NOTREACHED();
77 return std::string();
80 #if !defined (OS_CHROMEOS)
81 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
82 switch (field) {
83 case SIGNIN_TYPE:
84 return "Type";
85 case AUTHENTICATION_RESULT_RECEIVED:
86 return "Last Authentication Result Received";
87 case REFRESH_TOKEN_RECEIVED:
88 return "Last RefreshToken Received";
89 case GET_USER_INFO_STATUS:
90 return "Last OnGetUserInfo Received";
91 case UBER_TOKEN_STATUS:
92 return "Last OnUberToken Received";
93 case MERGE_SESSION_STATUS:
94 return "Last OnMergeSession Received";
95 case TIMED_FIELDS_END:
96 NOTREACHED();
97 return "Error";
99 NOTREACHED();
100 return "Error";
102 #endif // !defined (OS_CHROMEOS)
104 } // anonymous namespace
106 AboutSigninInternals::AboutSigninInternals(
107 ProfileOAuth2TokenService* token_service,
108 SigninManagerBase* signin_manager)
109 : token_service_(token_service),
110 signin_manager_(signin_manager),
111 client_(NULL) {}
113 AboutSigninInternals::~AboutSigninInternals() {}
115 void AboutSigninInternals::AddSigninObserver(
116 AboutSigninInternals::Observer* observer) {
117 signin_observers_.AddObserver(observer);
120 void AboutSigninInternals::RemoveSigninObserver(
121 AboutSigninInternals::Observer* observer) {
122 signin_observers_.RemoveObserver(observer);
125 void AboutSigninInternals::NotifySigninValueChanged(
126 const UntimedSigninStatusField& field,
127 const std::string& value) {
128 unsigned int field_index = field - UNTIMED_FIELDS_BEGIN;
129 DCHECK(field_index >= 0 &&
130 field_index < signin_status_.untimed_signin_fields.size());
132 signin_status_.untimed_signin_fields[field_index] = value;
134 // Also persist these values in the prefs.
135 const std::string pref_path = SigninStatusFieldToString(field);
136 client_->GetPrefs()->SetString(pref_path.c_str(), value);
138 NotifyObservers();
141 void AboutSigninInternals::NotifySigninValueChanged(
142 const TimedSigninStatusField& field,
143 const std::string& value) {
144 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
145 DCHECK(field_index >= 0 &&
146 field_index < signin_status_.timed_signin_fields.size());
148 Time now = Time::NowFromSystemTime();
149 std::string time_as_str =
150 base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now));
151 TimedSigninStatusValue timed_value(value, time_as_str);
153 signin_status_.timed_signin_fields[field_index] = timed_value;
155 // Also persist these values in the prefs.
156 const std::string value_pref = SigninStatusFieldToString(field) + ".value";
157 const std::string time_pref = SigninStatusFieldToString(field) + ".time";
158 client_->GetPrefs()->SetString(value_pref.c_str(), value);
159 client_->GetPrefs()->SetString(time_pref.c_str(), time_as_str);
161 NotifyObservers();
164 void AboutSigninInternals::RefreshSigninPrefs() {
165 // Since the AboutSigninInternals has a dependency on the SigninManager
166 // (as seen in the AboutSigninInternalsFactory) the SigninManager can have
167 // the AuthenticatedUsername set before AboutSigninInternals can observe it.
168 // For that scenario, read the AuthenticatedUsername if it exists.
169 if (signin_manager_->IsAuthenticated()) {
170 signin_status_.untimed_signin_fields[USERNAME] =
171 signin_manager_->GetAuthenticatedUsername();
174 // Return if no client exists. Can occur in unit tests.
175 if (!client_)
176 return;
178 PrefService* pref_service = client_->GetPrefs();
179 for (int i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
180 const std::string pref_path =
181 SigninStatusFieldToString(static_cast<UntimedSigninStatusField>(i));
183 signin_status_.untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN] =
184 pref_service->GetString(pref_path.c_str());
186 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
187 const std::string value_pref =
188 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
189 ".value";
190 const std::string time_pref =
191 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
192 ".time";
194 TimedSigninStatusValue value(pref_service->GetString(value_pref.c_str()),
195 pref_service->GetString(time_pref.c_str()));
196 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
199 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
201 NotifyObservers();
204 void AboutSigninInternals::Initialize(SigninClient* client) {
205 DCHECK(!client_);
206 client_ = client;
208 RefreshSigninPrefs();
210 signin_manager_->AddSigninDiagnosticsObserver(this);
211 token_service_->AddDiagnosticsObserver(this);
212 cookie_changed_subscription_ = client_->AddCookieChangedCallback(
213 GaiaUrls::GetInstance()->gaia_url(),
214 "LSID",
215 base::Bind(&AboutSigninInternals::OnCookieChanged,
216 base::Unretained(this)));
219 void AboutSigninInternals::Shutdown() {
220 signin_manager_->RemoveSigninDiagnosticsObserver(this);
221 token_service_->RemoveDiagnosticsObserver(this);
222 cookie_changed_subscription_.reset();
225 void AboutSigninInternals::NotifyObservers() {
226 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422460 is fixed.
227 tracked_objects::ScopedTracker tracking_profile(
228 FROM_HERE_WITH_EXPLICIT_FUNCTION(
229 "422460 AboutSigninInternals::NotifyObservers"));
231 scoped_ptr<base::DictionaryValue> signin_status_value =
232 signin_status_.ToValue(client_->GetProductVersion());
234 tracked_objects::ScopedTracker tracking_profile1(
235 FROM_HERE_WITH_EXPLICIT_FUNCTION(
236 "422460 AboutSigninInternals::NotifyObservers1"));
238 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
239 signin_observers_,
240 OnSigninStateChanged(signin_status_value.get()));
243 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
244 return signin_status_.ToValue(client_->GetProductVersion()).Pass();
247 void AboutSigninInternals::OnAccessTokenRequested(
248 const std::string& account_id,
249 const std::string& consumer_id,
250 const OAuth2TokenService::ScopeSet& scopes) {
251 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422460 is fixed.
252 tracked_objects::ScopedTracker tracking_profile(
253 FROM_HERE_WITH_EXPLICIT_FUNCTION(
254 "422460 AboutSigninInternals::OnAccessTokenRequested"));
256 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
257 if (token) {
258 *token = TokenInfo(consumer_id, scopes);
259 } else {
260 token = new TokenInfo(consumer_id, scopes);
261 signin_status_.token_info_map[account_id].push_back(token);
264 NotifyObservers();
267 void AboutSigninInternals::OnFetchAccessTokenComplete(
268 const std::string& account_id,
269 const std::string& consumer_id,
270 const OAuth2TokenService::ScopeSet& scopes,
271 GoogleServiceAuthError error,
272 base::Time expiration_time) {
273 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
274 if (!token) {
275 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
276 return;
279 token->receive_time = base::Time::Now();
280 token->error = error;
281 token->expiration_time = expiration_time;
283 NotifyObservers();
286 void AboutSigninInternals::OnTokenRemoved(
287 const std::string& account_id,
288 const OAuth2TokenService::ScopeSet& scopes) {
289 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
290 ++i) {
291 TokenInfo* token = signin_status_.token_info_map[account_id][i];
292 if (token->scopes == scopes)
293 token->Invalidate();
295 NotifyObservers();
298 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
299 NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
302 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
303 NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
306 void AboutSigninInternals::OnCookieChanged(const net::CanonicalCookie& cookie,
307 bool removed) {
308 DCHECK_EQ("LSID", cookie.Name());
309 DCHECK_EQ(GaiaUrls::GetInstance()->gaia_url().host(), cookie.Domain());
310 if (cookie.IsSecure() && cookie.IsHttpOnly()) {
311 GetCookieAccountsAsync();
315 void AboutSigninInternals::GetCookieAccountsAsync() {
316 // Don't bother calling /ListAccounts if no one will observe the response.
317 if (!gaia_fetcher_ && signin_observers_.might_have_observers()) {
318 // There is no list account request in flight.
319 gaia_fetcher_.reset(new GaiaAuthFetcher(
320 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext()));
321 gaia_fetcher_->StartListAccounts();
325 void AboutSigninInternals::OnListAccountsSuccess(const std::string& data) {
326 gaia_fetcher_.reset();
328 // Get account information from response data.
329 std::vector<std::pair<std::string, bool> > gaia_accounts;
330 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
331 if (!valid_json) {
332 VLOG(1) << "AboutSigninInternals::OnListAccountsSuccess: parsing error";
333 } else {
334 OnListAccountsComplete(gaia_accounts);
338 void AboutSigninInternals::OnListAccountsFailure(
339 const GoogleServiceAuthError& error) {
340 gaia_fetcher_.reset();
341 VLOG(1) << "AboutSigninInternals::OnListAccountsFailure:" << error.ToString();
344 void AboutSigninInternals::OnListAccountsComplete(
345 std::vector<std::pair<std::string, bool> >& gaia_accounts) {
346 base::DictionaryValue signin_status;
347 base::ListValue* cookie_info = new base::ListValue();
348 signin_status.Set("cookie_info", cookie_info);
350 for (size_t i = 0; i < gaia_accounts.size(); ++i) {
351 AddCookieEntry(cookie_info,
352 gaia_accounts[i].first,
353 gaia_accounts[i].second ? "Valid" : "Invalid");
356 if (gaia_accounts.size() == 0)
357 AddCookieEntry(cookie_info, "No Accounts Present.", "");
359 // Update the observers that the cookie's accounts are updated.
360 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
361 signin_observers_,
362 OnCookieAccountsFetched(&signin_status));
365 AboutSigninInternals::TokenInfo::TokenInfo(
366 const std::string& consumer_id,
367 const OAuth2TokenService::ScopeSet& scopes)
368 : consumer_id(consumer_id),
369 scopes(scopes),
370 request_time(base::Time::Now()),
371 error(GoogleServiceAuthError::AuthErrorNone()),
372 removed_(false) {}
374 AboutSigninInternals::TokenInfo::~TokenInfo() {}
376 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
377 const TokenInfo* b) {
378 return a->consumer_id < b->consumer_id ||
379 (a->consumer_id == b->consumer_id && a->scopes < b->scopes);
382 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
384 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
385 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
386 token_info->SetString("service", consumer_id);
388 std::string scopes_str;
389 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
390 it != scopes.end();
391 ++it) {
392 scopes_str += *it + "<br/>";
394 token_info->SetString("scopes", scopes_str);
395 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
397 if (removed_) {
398 token_info->SetString("status", "Token was revoked.");
399 } else if (!receive_time.is_null()) {
400 if (error == GoogleServiceAuthError::AuthErrorNone()) {
401 bool token_expired = expiration_time < base::Time::Now();
402 std::string status_str = "";
403 if (token_expired)
404 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
405 base::StringAppendF(&status_str,
406 "Received token at %s. Expire at %s",
407 GetTimeStr(receive_time).c_str(),
408 GetTimeStr(expiration_time).c_str());
409 if (token_expired)
410 base::StringAppendF(&status_str, "</p>");
411 token_info->SetString("status", status_str);
412 } else {
413 token_info->SetString(
414 "status",
415 base::StringPrintf("Failure: %s", error.ToString().c_str()));
417 } else {
418 token_info->SetString("status", "Waiting for response");
421 return token_info.release();
424 AboutSigninInternals::SigninStatus::SigninStatus()
425 : untimed_signin_fields(UNTIMED_FIELDS_COUNT),
426 timed_signin_fields(TIMED_FIELDS_COUNT) {}
428 AboutSigninInternals::SigninStatus::~SigninStatus() {
429 for (TokenInfoMap::iterator it = token_info_map.begin();
430 it != token_info_map.end();
431 ++it) {
432 STLDeleteElements(&it->second);
436 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
437 const std::string& account_id,
438 const std::string& consumer_id,
439 const OAuth2TokenService::ScopeSet& scopes) {
440 for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
441 TokenInfo* tmp = token_info_map[account_id][i];
442 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
443 return tmp;
445 return NULL;
448 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
449 std::string product_version) {
450 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
451 base::ListValue* signin_info = new base::ListValue();
452 signin_status->Set("signin_info", signin_info);
454 // A summary of signin related info first.
455 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
456 const std::string signin_status_string =
457 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty()
458 ? "Not Signed In"
459 : "Signed In";
460 AddSectionEntry(basic_info, "Chrome Version", product_version);
461 AddSectionEntry(basic_info, "Signin Status", signin_status_string);
462 AddSectionEntry(basic_info, "Web Based Signin Enabled?",
463 switches::IsEnableWebBasedSignin() == true ? "True" : "False");
464 AddSectionEntry(basic_info, "Webview Based Signin Enabled?",
465 switches::IsEnableWebviewBasedSignin() == true ? "True" : "False");
466 AddSectionEntry(basic_info, "New Avatar Menu Enabled?",
467 switches::IsNewAvatarMenu() == true ? "True" : "False");
468 AddSectionEntry(basic_info, "New Profile Management Enabled?",
469 switches::IsNewProfileManagement() == true ? "True" : "False");
470 AddSectionEntry(basic_info, "Account Consistency Enabled?",
471 switches::IsEnableAccountConsistency() == true ? "True" : "False");
473 // Only add username. SID and LSID have moved to tokens section.
474 const std::string field =
475 SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
476 AddSectionEntry(basic_info,
477 field,
478 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
480 #if !defined(OS_CHROMEOS)
481 // Time and status information of the possible sign in types.
482 base::ListValue* detailed_info =
483 AddSection(signin_info, "Last Signin Details");
484 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
485 const std::string status_field_label =
486 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
488 AddSectionEntry(detailed_info,
489 status_field_label,
490 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
491 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
493 #endif // !defined(OS_CHROMEOS)
495 // Token information for all services.
496 base::ListValue* token_info = new base::ListValue();
497 signin_status->Set("token_info", token_info);
498 for (TokenInfoMap::iterator it = token_info_map.begin();
499 it != token_info_map.end();
500 ++it) {
501 base::ListValue* token_details = AddSection(token_info, it->first);
503 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
504 const std::vector<TokenInfo*>& tokens = it->second;
505 for (size_t i = 0; i < tokens.size(); ++i) {
506 base::DictionaryValue* token_info = tokens[i]->ToValue();
507 token_details->Append(token_info);
511 return signin_status.Pass();