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 "chrome/browser/signin/about_signin_internals.h"
7 #include "base/debug/trace_event.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 "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service.h"
16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17 #include "chrome/browser/signin/signin_internals_util.h"
18 #include "chrome/browser/signin/signin_manager.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "google_apis/gaia/gaia_constants.h"
23 using namespace signin_internals_util
;
27 std::string
GetTimeStr(base::Time time
) {
28 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time
));
31 base::ListValue
* AddSection(base::ListValue
* parent_list
,
32 const std::string
& title
) {
33 scoped_ptr
<base::DictionaryValue
> section(new base::DictionaryValue());
34 base::ListValue
* section_contents
= new base::ListValue();
36 section
->SetString("title", title
);
37 section
->Set("data", section_contents
);
38 parent_list
->Append(section
.release());
39 return section_contents
;
42 void AddSectionEntry(base::ListValue
* section_list
,
43 const std::string
& field_name
,
44 const std::string
& field_val
) {
45 scoped_ptr
<base::DictionaryValue
> entry(new base::DictionaryValue());
46 entry
->SetString("label", field_name
);
47 entry
->SetString("value", field_val
);
48 section_list
->Append(entry
.release());
51 std::string
SigninStatusFieldToLabel(UntimedSigninStatusField field
) {
55 case UNTIMED_FIELDS_END
:
63 TimedSigninStatusValue
SigninStatusFieldToLabel(
64 TimedSigninStatusField field
) {
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");
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
:
86 return TimedSigninStatusValue("Error", std::string());
89 return TimedSigninStatusValue("Error", std::string());
92 // Returns a string describing the chrome version environment. Version format:
93 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
94 // If version information is unavailable, returns "invalid."
95 std::string
GetVersionString() {
96 chrome::VersionInfo chrome_version
;
97 if (!chrome_version
.is_valid())
99 return chrome_version
.CreateVersionString();
102 } // anonymous namespace
104 AboutSigninInternals::AboutSigninInternals() : profile_(NULL
) {
107 AboutSigninInternals::~AboutSigninInternals() {
110 void AboutSigninInternals::AddSigninObserver(
111 AboutSigninInternals::Observer
* observer
) {
112 signin_observers_
.AddObserver(observer
);
115 void AboutSigninInternals::RemoveSigninObserver(
116 AboutSigninInternals::Observer
* observer
) {
117 signin_observers_
.RemoveObserver(observer
);
120 void AboutSigninInternals::NotifySigninValueChanged(
121 const UntimedSigninStatusField
& field
,
122 const std::string
& value
) {
123 unsigned int field_index
= field
- UNTIMED_FIELDS_BEGIN
;
124 DCHECK(field_index
>= 0 &&
125 field_index
< signin_status_
.untimed_signin_fields
.size());
127 signin_status_
.untimed_signin_fields
[field_index
] = value
;
129 // Also persist these values in the prefs.
130 const std::string pref_path
= SigninStatusFieldToString(field
);
131 profile_
->GetPrefs()->SetString(pref_path
.c_str(), value
);
136 void AboutSigninInternals::NotifySigninValueChanged(
137 const TimedSigninStatusField
& field
,
138 const std::string
& value
) {
139 unsigned int field_index
= field
- TIMED_FIELDS_BEGIN
;
140 DCHECK(field_index
>= 0 &&
141 field_index
< signin_status_
.timed_signin_fields
.size());
143 Time now
= Time::NowFromSystemTime();
144 std::string time_as_str
=
145 base::UTF16ToUTF8(base::TimeFormatFriendlyDate(now
));
146 TimedSigninStatusValue
timed_value(value
, time_as_str
);
148 signin_status_
.timed_signin_fields
[field_index
] = timed_value
;
150 // Also persist these values in the prefs.
151 const std::string value_pref
= SigninStatusFieldToString(field
) + ".value";
152 const std::string time_pref
= SigninStatusFieldToString(field
) + ".time";
153 profile_
->GetPrefs()->SetString(value_pref
.c_str(), value
);
154 profile_
->GetPrefs()->SetString(time_pref
.c_str(), time_as_str
);
159 void AboutSigninInternals::RefreshSigninPrefs() {
160 // Return if no profile exists. Can occur in unit tests.
164 PrefService
* pref_service
= profile_
->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
));
169 signin_status_
.untimed_signin_fields
[i
- UNTIMED_FIELDS_BEGIN
] =
170 pref_service
->GetString(pref_path
.c_str());
172 for (int i
= TIMED_FIELDS_BEGIN
; i
< TIMED_FIELDS_END
; ++i
) {
173 const std::string value_pref
= SigninStatusFieldToString(
174 static_cast<TimedSigninStatusField
>(i
)) + ".value";
175 const std::string time_pref
= SigninStatusFieldToString(
176 static_cast<TimedSigninStatusField
>(i
)) + ".time";
178 TimedSigninStatusValue
value(pref_service
->GetString(value_pref
.c_str()),
179 pref_service
->GetString(time_pref
.c_str()));
180 signin_status_
.timed_signin_fields
[i
- TIMED_FIELDS_BEGIN
] = value
;
183 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
188 void AboutSigninInternals::Initialize(Profile
* profile
) {
192 RefreshSigninPrefs();
194 SigninManagerFactory::GetForProfile(profile
)->
195 AddSigninDiagnosticsObserver(this);
196 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
)->
197 AddDiagnosticsObserver(this);
200 void AboutSigninInternals::Shutdown() {
201 SigninManagerFactory::GetForProfile(profile_
)->
202 RemoveSigninDiagnosticsObserver(this);
203 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
)->
204 RemoveDiagnosticsObserver(this);
208 void AboutSigninInternals::NotifyObservers() {
209 FOR_EACH_OBSERVER(AboutSigninInternals::Observer
,
211 OnSigninStateChanged(signin_status_
.ToValue()));
214 scoped_ptr
<base::DictionaryValue
> AboutSigninInternals::GetSigninStatus() {
215 return signin_status_
.ToValue().Pass();
218 void AboutSigninInternals::OnAccessTokenRequested(
219 const std::string
& account_id
,
220 const std::string
& consumer_id
,
221 const OAuth2TokenService::ScopeSet
& scopes
) {
222 TokenInfo
* token
= signin_status_
.FindToken(account_id
, consumer_id
, scopes
);
224 *token
= TokenInfo(consumer_id
, scopes
);
226 token
= new TokenInfo(consumer_id
, scopes
);
227 signin_status_
.token_info_map
[account_id
].push_back(token
);
233 void AboutSigninInternals::OnFetchAccessTokenComplete(
234 const std::string
& account_id
,
235 const std::string
& consumer_id
,
236 const OAuth2TokenService::ScopeSet
& scopes
,
237 GoogleServiceAuthError error
,
238 base::Time expiration_time
) {
239 TokenInfo
* token
= signin_status_
.FindToken(account_id
, consumer_id
, scopes
);
241 DVLOG(1) << "Can't find token: " << account_id
<< ", " << consumer_id
;
245 token
->receive_time
= base::Time::Now();
246 token
->error
= error
;
247 token
->expiration_time
= expiration_time
;
252 void AboutSigninInternals::OnTokenRemoved(
253 const std::string
& account_id
,
254 const OAuth2TokenService::ScopeSet
& scopes
) {
255 for (size_t i
= 0; i
< signin_status_
.token_info_map
[account_id
].size();
257 TokenInfo
* token
= signin_status_
.token_info_map
[account_id
][i
];
258 if (token
->scopes
== scopes
)
264 AboutSigninInternals::TokenInfo::TokenInfo(
265 const std::string
& consumer_id
,
266 const OAuth2TokenService::ScopeSet
& scopes
)
267 : consumer_id(consumer_id
),
269 request_time(base::Time::Now()),
270 error(GoogleServiceAuthError::AuthErrorNone()),
274 AboutSigninInternals::TokenInfo::~TokenInfo() {}
276 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo
* a
,
277 const TokenInfo
* b
) {
278 return a
->consumer_id
< b
->consumer_id
|| a
->scopes
< b
->scopes
;
281 void AboutSigninInternals::TokenInfo::Invalidate() {
285 base::DictionaryValue
* AboutSigninInternals::TokenInfo::ToValue() const {
286 scoped_ptr
<base::DictionaryValue
> token_info(new base::DictionaryValue());
287 token_info
->SetString("service", consumer_id
);
289 std::string scopes_str
;
290 for (OAuth2TokenService::ScopeSet::const_iterator it
= scopes
.begin();
291 it
!= scopes
.end(); ++it
) {
292 scopes_str
+= *it
+ "<br/>";
294 token_info
->SetString("scopes", scopes_str
);
295 token_info
->SetString("request_time", GetTimeStr(request_time
).c_str());
298 token_info
->SetString("status", "Token was revoked.");
299 } else if (!receive_time
.is_null()) {
300 if (error
== GoogleServiceAuthError::AuthErrorNone()) {
301 bool token_expired
= expiration_time
< base::Time::Now();
302 std::string status_str
= "";
304 status_str
= "<p style=\"color: #ffffff; background-color: #ff0000\">";
305 base::StringAppendF(&status_str
,
306 "Received token at %s. Expire at %s",
307 GetTimeStr(receive_time
).c_str(),
308 GetTimeStr(expiration_time
).c_str());
310 base::StringAppendF(&status_str
, "</p>");
311 token_info
->SetString("status", status_str
);
313 token_info
->SetString(
315 base::StringPrintf("Failure: %s", error
.error_message().c_str()));
318 token_info
->SetString("status", "Waiting for response");
321 return token_info
.release();
324 AboutSigninInternals::SigninStatus::SigninStatus()
325 :untimed_signin_fields(UNTIMED_FIELDS_COUNT
),
326 timed_signin_fields(TIMED_FIELDS_COUNT
) {
329 AboutSigninInternals::SigninStatus::~SigninStatus() {
330 for (TokenInfoMap::iterator it
= token_info_map
.begin();
331 it
!= token_info_map
.end(); ++it
) {
332 STLDeleteElements(&it
->second
);
336 AboutSigninInternals::TokenInfo
*
337 AboutSigninInternals::SigninStatus::FindToken(
338 const std::string
& account_id
,
339 const std::string
& consumer_id
,
340 const OAuth2TokenService::ScopeSet
& scopes
) {
341 for (size_t i
= 0; i
< token_info_map
[account_id
].size();
343 TokenInfo
* tmp
= token_info_map
[account_id
][i
];
344 if (tmp
->consumer_id
== consumer_id
&& tmp
->scopes
== scopes
)
350 scoped_ptr
<base::DictionaryValue
>
351 AboutSigninInternals::SigninStatus::ToValue() {
352 scoped_ptr
<base::DictionaryValue
> signin_status(new base::DictionaryValue());
353 base::ListValue
* signin_info
= new base::ListValue();
354 signin_status
->Set("signin_info", signin_info
);
356 // A summary of signin related info first.
357 base::ListValue
* basic_info
= AddSection(signin_info
, "Basic Information");
358 const std::string signin_status_string
=
359 untimed_signin_fields
[USERNAME
- UNTIMED_FIELDS_BEGIN
].empty() ?
360 "Not Signed In" : "Signed In";
361 AddSectionEntry(basic_info
, "Chrome Version", GetVersionString());
362 AddSectionEntry(basic_info
, "Signin Status", signin_status_string
);
364 // Only add username. SID and LSID have moved to tokens section.
365 const std::string field
=
366 SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField
>(USERNAME
));
370 untimed_signin_fields
[USERNAME
- UNTIMED_FIELDS_BEGIN
]);
372 // Time and status information of the possible sign in types.
373 base::ListValue
* detailed_info
= AddSection(signin_info
,
374 "Last Signin Details");
375 for (int i
= TIMED_FIELDS_BEGIN
; i
< TIMED_FIELDS_END
; ++i
) {
376 const std::string value_field
=
377 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField
>(i
)).first
;
378 const std::string time_field
=
379 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField
>(i
)).second
;
381 AddSectionEntry(detailed_info
, value_field
,
382 timed_signin_fields
[i
- TIMED_FIELDS_BEGIN
].first
);
383 AddSectionEntry(detailed_info
, time_field
,
384 timed_signin_fields
[i
- TIMED_FIELDS_BEGIN
].second
);
387 // Token information for all services.
388 base::ListValue
* token_info
= new base::ListValue();
389 signin_status
->Set("token_info", token_info
);
390 for (TokenInfoMap::iterator it
= token_info_map
.begin();
391 it
!= token_info_map
.end(); ++it
) {
392 base::ListValue
* token_details
= AddSection(token_info
, it
->first
);
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
);
402 return signin_status
.Pass();