blob: 94e1f100982cdcbea6fd4650bdbcb7ad5676a3ed [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/signin/signin_internals_util.h"
#include <sstream>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/web_contents.h"
#include "crypto/sha2.h"
#include "google_apis/gaia/gaia_constants.h"
namespace signin_internals_util {
const char kSigninPrefPrefix[] = "google.services.signin.";
const char kTokenPrefPrefix[] = "google.services.signin.tokens.";
const char kOperationsBaseToken[] = "OperationsBase";
const char kUserPolicySigninServiceToken[] = "UserCloudPolicyManagerToken";
const char kProfileDownloaderToken[] = "ProfileDownloader";
// TODO(vishwath): These two services need their information plumbed to
// about:signin-internals.
const char kObfuscatedGaiaIdFetcherToken[] = "ObfuscatedGaiaIdFetcher";
const char kOAuth2MintTokenFlowToken[] = "OAuth2MintTokenFlow";
const char kSIDToken[] = "SID";
const char kLSIDToken[] = "LSID";
const char* kTokenPrefsArray[] = {
GaiaConstants::kSyncService,
GaiaConstants::kGaiaOAuth2LoginRefreshToken,
kSIDToken,
kLSIDToken,
kOperationsBaseToken,
kUserPolicySigninServiceToken,
kProfileDownloaderToken,
kObfuscatedGaiaIdFetcherToken,
kOAuth2MintTokenFlowToken
};
const size_t kNumTokenPrefs = arraysize(kTokenPrefsArray);
TokenInfo::TokenInfo(const std::string& truncated_token,
const std::string& status,
const std::string& time,
const int64& time_internal,
const std::string& service)
: truncated_token(truncated_token),
status(status),
time(time),
time_internal(time_internal),
service(service) {
// This should be a truncated and hashed token.
DCHECK_LE(truncated_token.length(), kTruncateTokenStringLength);
}
TokenInfo::TokenInfo() {
}
TokenInfo::~TokenInfo() {
}
DictionaryValue* TokenInfo::ToValue() {
scoped_ptr<DictionaryValue> token_info(new DictionaryValue());
token_info->SetString("service", service);
token_info->SetString("token", truncated_token);
token_info->SetString("status", status);
token_info->SetString("time", time);
return token_info.release();
}
#define ENUM_CASE(x) case x: return (std::string(kSigninPrefPrefix) + #x)
std::string SigninStatusFieldToString(UntimedSigninStatusField field) {
switch (field) {
ENUM_CASE(USERNAME);
ENUM_CASE(SID);
ENUM_CASE(LSID);
case UNTIMED_FIELDS_END:
NOTREACHED();
return std::string();
}
NOTREACHED();
return std::string();
}
std::string SigninStatusFieldToString(TimedSigninStatusField field) {
switch (field) {
ENUM_CASE(SIGNIN_TYPE);
ENUM_CASE(CLIENT_LOGIN_STATUS);
ENUM_CASE(OAUTH_LOGIN_STATUS);
ENUM_CASE(GET_USER_INFO_STATUS);
case TIMED_FIELDS_END:
NOTREACHED();
return std::string();
}
NOTREACHED();
return std::string();
}
SigninStatus::SigninStatus()
:untimed_signin_fields(UNTIMED_FIELDS_COUNT),
timed_signin_fields(TIMED_FIELDS_COUNT) {
}
SigninStatus::~SigninStatus() {
}
std::string TokenPrefPath(const std::string& token_name) {
return std::string(kTokenPrefPrefix) + token_name;
}
namespace {
ListValue* AddSection(ListValue* parent_list, const std::string& title) {
scoped_ptr<DictionaryValue> section(new DictionaryValue());
ListValue* section_contents = new ListValue();
section->SetString("title", title);
section->Set("data", section_contents);
parent_list->Append(section.release());
return section_contents;
}
void AddSectionEntry(ListValue* section_list,
const std::string& field_name,
const std::string& field_val) {
scoped_ptr<DictionaryValue> entry(new DictionaryValue());
entry->SetString("label", field_name);
entry->SetString("value", field_val);
section_list->Append(entry.release());
}
void AddSectionEntry(ListValue* section_list,
const std::string& field_name,
uint32 field_val) {
std::stringstream ss;
ss << field_val;
scoped_ptr<DictionaryValue> entry(new DictionaryValue());
entry->SetString("label", field_name);
entry->SetString("value", ss.str());
section_list->Append(entry.release());
}
// Returns a string describing the chrome version environment. Version format:
// <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
// If version information is unavailable, returns "invalid."
std::string GetVersionString() {
// Build a version string that matches MakeUserAgentForSyncApi with the
// addition of channel info and proper OS names.
chrome::VersionInfo chrome_version;
if (!chrome_version.is_valid())
return "invalid";
// GetVersionStringModifier returns empty string for stable channel or
// unofficial builds, the channel string otherwise. We want to have "-devel"
// for unofficial builds only.
std::string version_modifier =
chrome::VersionInfo::GetVersionStringModifier();
if (version_modifier.empty()) {
if (chrome::VersionInfo::GetChannel() !=
chrome::VersionInfo::CHANNEL_STABLE) {
version_modifier = "-devel";
}
} else {
version_modifier = " " + version_modifier;
}
return chrome_version.Name() + " " + chrome_version.OSType() + " " +
chrome_version.Version() + " (" + chrome_version.LastChange() + ")" +
version_modifier;
}
std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
switch (field) {
case USERNAME:
return "User Id";
case LSID:
return "Lsid (Hash)";
case SID:
return "Sid (Hash)";
case UNTIMED_FIELDS_END:
NOTREACHED();
return std::string();
}
NOTREACHED();
return std::string();
}
TimedSigninStatusValue SigninStatusFieldToLabel(
TimedSigninStatusField field) {
switch (field) {
case SIGNIN_TYPE:
return TimedSigninStatusValue("Type", "Time");
case CLIENT_LOGIN_STATUS:
return TimedSigninStatusValue("Last OnClientLogin Status",
"Last OnClientLogin Time");
case OAUTH_LOGIN_STATUS:
return TimedSigninStatusValue("Last OnOAuthLogin Status",
"Last OnOAuthLogin Time");
case GET_USER_INFO_STATUS:
return TimedSigninStatusValue("Last OnGetUserInfo Status",
"Last OnGetUserInfo Time");
case TIMED_FIELDS_END:
NOTREACHED();
return TimedSigninStatusValue("Error", std::string());
}
NOTREACHED();
return TimedSigninStatusValue("Error", std::string());
}
} // namespace
scoped_ptr<DictionaryValue> SigninStatus::ToValue() {
scoped_ptr<DictionaryValue> signin_status(new DictionaryValue());
ListValue* signin_info = new ListValue();
signin_status->Set("signin_info", signin_info);
// A summary of signin related info first.
ListValue* basic_info = AddSection(signin_info, "Basic Information");
const std::string signin_status_string =
untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty() ?
"Not Signed In" : "Signed In";
AddSectionEntry(basic_info, "Chrome Version", GetVersionString());
AddSectionEntry(basic_info, "Signin Status", signin_status_string);
// Only add username. SID and LSID have moved to tokens section.
const std::string field =
SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
AddSectionEntry(
basic_info,
field,
untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
// Time and status information of the possible sign in types.
ListValue* detailed_info = AddSection(signin_info, "Last Signin Details");
for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
const std::string value_field =
SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).first;
const std::string time_field =
SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).second;
AddSectionEntry(detailed_info, value_field,
timed_signin_fields[i - TIMED_FIELDS_BEGIN].first);
AddSectionEntry(detailed_info, time_field,
timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
}
// Token information for all services.
ListValue* token_info = new ListValue();
ListValue* token_details = AddSection(token_info, "Token Details");
signin_status->Set("token_info", token_info);
for (std::map<std::string, TokenInfo>::iterator it = token_info_map.begin();
it != token_info_map.end(); ++it) {
DictionaryValue* token_info = it->second.ToValue();
token_details->Append(token_info);
}
return signin_status.Pass();
}
// Gets the first few hex characters of the SHA256 hash of the passed in string.
// These are enough to perform equality checks across a single users tokens,
// while preventing outsiders from reverse-engineering the actual token from
// the displayed value.
// Note that for readability (in about:signin-internals), an empty string
// is not hashed, but simply returned as an empty string.
std::string GetTruncatedHash(const std::string& str) {
if (str.empty())
return str;
// Since each character in the hash string generates two hex charaters
// we only need half as many charaters in |hash_val| as hex characters
// returned.
const int kTruncateSize = kTruncateTokenStringLength / 2;
char hash_val[kTruncateSize];
crypto::SHA256HashString(str, &hash_val[0], kTruncateSize);
return StringToLowerASCII(base::HexEncode(&hash_val[0], kTruncateSize));
}
} // namespace signin_internals_util