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