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