Home | History | Annotate | Download | only in signin
      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 "chrome/browser/signin/signin_internals_util.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/logging.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/common/chrome_version_info.h"
     14 #include "chrome/common/url_constants.h"
     15 #include "content/public/browser/web_contents.h"
     16 #include "crypto/sha2.h"
     17 #include "google_apis/gaia/gaia_constants.h"
     18 
     19 namespace signin_internals_util {
     20 
     21 const char kSigninPrefPrefix[] = "google.services.signin.";
     22 const char kTokenPrefPrefix[] = "google.services.signin.tokens.";
     23 const char kOperationsBaseToken[] = "OperationsBase";
     24 const char kUserPolicySigninServiceToken[] = "UserCloudPolicyManagerToken";
     25 const char kProfileDownloaderToken[] = "ProfileDownloader";
     26 
     27 // TODO(vishwath): These two services need their information plumbed to
     28 // about:signin-internals.
     29 const char kObfuscatedGaiaIdFetcherToken[] = "ObfuscatedGaiaIdFetcher";
     30 const char kOAuth2MintTokenFlowToken[] = "OAuth2MintTokenFlow";
     31 const char* kTokenPrefsArray[] = {
     32   GaiaConstants::kSyncService,
     33   GaiaConstants::kLSOService,
     34   GaiaConstants::kGaiaOAuth2LoginRefreshToken,
     35   kOperationsBaseToken,
     36   kUserPolicySigninServiceToken,
     37   kProfileDownloaderToken,
     38   kObfuscatedGaiaIdFetcherToken,
     39   kOAuth2MintTokenFlowToken
     40 };
     41 
     42 const size_t kNumTokenPrefs = arraysize(kTokenPrefsArray);
     43 
     44 
     45 namespace {
     46 
     47 // Gets the first 6 hex characters of the SHA256 hash of the passed in string.
     48 // These are enough to perform equality checks across a single users tokens,
     49 // while preventing outsiders from reverse-engineering the actual token from
     50 // the displayed value.
     51 // Note that for readability (in about:signin-internals), an empty string
     52 // is not hashed, but simply returned as an empty string.
     53 const size_t kTruncateSize = 3;
     54 std::string GetTruncatedHash(const std::string& str) {
     55   if (str.empty())
     56     return str;
     57 
     58   char hash_val[kTruncateSize];
     59   crypto::SHA256HashString(str, &hash_val[0], kTruncateSize);
     60   return StringToLowerASCII(base::HexEncode(&hash_val[0], kTruncateSize));
     61 }
     62 
     63 } // namespace
     64 
     65 TokenInfo::TokenInfo(const std::string& token,
     66                      const std::string& status,
     67                      const std::string& time,
     68                      const int64& time_internal,
     69                      const std::string& service)
     70     : token(token),
     71       status(status),
     72       time(time),
     73       time_internal(time_internal),
     74       service(service) {
     75 }
     76 
     77 TokenInfo::TokenInfo() {
     78 }
     79 
     80 TokenInfo::~TokenInfo() {
     81 }
     82 
     83 DictionaryValue* TokenInfo::ToValue() {
     84   scoped_ptr<DictionaryValue> token_info(new DictionaryValue());
     85   token_info->SetString("service", service);
     86   token_info->SetString("token", GetTruncatedHash(token));
     87   token_info->SetString("status", status);
     88   token_info->SetString("time", time);
     89 
     90   return token_info.release();
     91 }
     92 
     93 #define ENUM_CASE(x) case x: return (std::string(kSigninPrefPrefix) + #x)
     94 std::string SigninStatusFieldToString(UntimedSigninStatusField field) {
     95   switch (field) {
     96     ENUM_CASE(USERNAME);
     97     ENUM_CASE(SID);
     98     ENUM_CASE(LSID);
     99     case UNTIMED_FIELDS_END:
    100       NOTREACHED();
    101       return std::string();
    102   }
    103 
    104   NOTREACHED();
    105   return std::string();
    106 }
    107 
    108 std::string SigninStatusFieldToString(TimedSigninStatusField field) {
    109   switch (field) {
    110     ENUM_CASE(SIGNIN_TYPE);
    111     ENUM_CASE(CLIENT_LOGIN_STATUS);
    112     ENUM_CASE(OAUTH_LOGIN_STATUS);
    113     ENUM_CASE(GET_USER_INFO_STATUS);
    114     case TIMED_FIELDS_END:
    115       NOTREACHED();
    116       return std::string();
    117   }
    118 
    119   NOTREACHED();
    120   return std::string();
    121 }
    122 
    123 SigninStatus::SigninStatus()
    124     :untimed_signin_fields(UNTIMED_FIELDS_COUNT),
    125      timed_signin_fields(TIMED_FIELDS_COUNT) {
    126 }
    127 
    128 SigninStatus::~SigninStatus() {
    129 }
    130 
    131 std::string TokenPrefPath(const std::string& token_name) {
    132   return std::string(kTokenPrefPrefix) + token_name;
    133 }
    134 
    135 namespace {
    136 
    137 ListValue* AddSection(ListValue* parent_list, const std::string& title) {
    138   scoped_ptr<DictionaryValue> section(new DictionaryValue());
    139   ListValue* section_contents = new ListValue();
    140 
    141   section->SetString("title", title);
    142   section->Set("data", section_contents);
    143   parent_list->Append(section.release());
    144   return section_contents;
    145 }
    146 
    147 void AddSectionEntry(ListValue* section_list,
    148                      const std::string& field_name,
    149                      const std::string& field_val) {
    150   scoped_ptr<DictionaryValue> entry(new DictionaryValue());
    151   entry->SetString("label", field_name);
    152   entry->SetString("value", field_val);
    153   section_list->Append(entry.release());
    154 }
    155 
    156 
    157 void AddSectionEntry(ListValue* section_list,
    158                      const std::string& field_name,
    159                      uint32 field_val) {
    160   std::stringstream ss;
    161   ss << field_val;
    162   scoped_ptr<DictionaryValue> entry(new DictionaryValue());
    163   entry->SetString("label", field_name);
    164   entry->SetString("value", ss.str());
    165   section_list->Append(entry.release());
    166 }
    167 
    168 // Returns a string describing the chrome version environment. Version format:
    169 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
    170 // If version information is unavailable, returns "invalid."
    171 std::string GetVersionString() {
    172   // Build a version string that matches MakeUserAgentForSyncApi with the
    173   // addition of channel info and proper OS names.
    174   chrome::VersionInfo chrome_version;
    175   if (!chrome_version.is_valid())
    176     return "invalid";
    177   // GetVersionStringModifier returns empty string for stable channel or
    178   // unofficial builds, the channel string otherwise. We want to have "-devel"
    179   // for unofficial builds only.
    180   std::string version_modifier =
    181       chrome::VersionInfo::GetVersionStringModifier();
    182   if (version_modifier.empty()) {
    183     if (chrome::VersionInfo::GetChannel() !=
    184             chrome::VersionInfo::CHANNEL_STABLE) {
    185       version_modifier = "-devel";
    186     }
    187   } else {
    188     version_modifier = " " + version_modifier;
    189   }
    190   return chrome_version.Name() + " " + chrome_version.OSType() + " " +
    191       chrome_version.Version() + " (" + chrome_version.LastChange() + ")" +
    192       version_modifier;
    193 }
    194 
    195 
    196 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
    197   switch (field) {
    198     case USERNAME:
    199       return "User Id";
    200     case LSID:
    201       return "Lsid (Hash)";
    202     case SID:
    203       return "Sid (Hash)";
    204     case UNTIMED_FIELDS_END:
    205       NOTREACHED();
    206       return std::string();
    207   }
    208   NOTREACHED();
    209   return std::string();
    210 }
    211 
    212 TimedSigninStatusValue SigninStatusFieldToLabel(
    213     TimedSigninStatusField field) {
    214   switch (field) {
    215     case SIGNIN_TYPE:
    216       return TimedSigninStatusValue("Type", "Time");
    217     case CLIENT_LOGIN_STATUS:
    218       return TimedSigninStatusValue("Last OnClientLogin Status",
    219                                     "Last OnClientLogin Time");
    220     case OAUTH_LOGIN_STATUS:
    221       return TimedSigninStatusValue("Last OnOAuthLogin Status",
    222                                     "Last OnOAuthLogin Time");
    223 
    224     case GET_USER_INFO_STATUS:
    225       return TimedSigninStatusValue("Last OnGetUserInfo Status",
    226                                     "Last OnGetUserInfo Time");
    227     case TIMED_FIELDS_END:
    228       NOTREACHED();
    229       return TimedSigninStatusValue("Error", std::string());
    230   }
    231   NOTREACHED();
    232   return TimedSigninStatusValue("Error", std::string());
    233 }
    234 
    235 } //  namespace
    236 
    237 scoped_ptr<DictionaryValue> SigninStatus::ToValue() {
    238   scoped_ptr<DictionaryValue> signin_status(new DictionaryValue());
    239   ListValue* signin_info = new ListValue();
    240   signin_status->Set("signin_info", signin_info);
    241 
    242   // A summary of signin related info first.
    243   ListValue* basic_info = AddSection(signin_info, "Basic Information");
    244   const std::string signin_status_string =
    245       untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty() ?
    246       "Not Signed In" : "Signed In";
    247   AddSectionEntry(basic_info, "Chrome Version", GetVersionString());
    248   AddSectionEntry(basic_info, "Signin Status", signin_status_string);
    249   int i;
    250   for (i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
    251     const std::string field =
    252         SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(i));
    253     if (i == SID || i == LSID) {
    254       AddSectionEntry(
    255           basic_info,
    256           field,
    257           GetTruncatedHash(untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN]));
    258     } else {
    259       AddSectionEntry(
    260           basic_info,
    261           field,
    262           untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN]);
    263     }
    264   }
    265 
    266   // Time and status information of the possible sign in types.
    267   ListValue* detailed_info = AddSection(signin_info, "Last Signin Details");
    268   for (; i < TIMED_FIELDS_END; ++i) {
    269     const std::string value_field =
    270         SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).first;
    271     const std::string time_field =
    272         SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).second;
    273 
    274     AddSectionEntry(detailed_info, value_field,
    275                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].first);
    276     AddSectionEntry(detailed_info, time_field,
    277                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
    278   }
    279 
    280   // Token information for all services.
    281   ListValue* token_info = new ListValue();
    282   ListValue* token_details = AddSection(token_info, "Token Details");
    283   signin_status->Set("token_info", token_info);
    284   for (std::map<std::string, TokenInfo>::iterator it = token_info_map.begin();
    285        it != token_info_map.end(); ++it) {
    286     DictionaryValue* token_info = it->second.ToValue();
    287     token_details->Append(token_info);
    288   }
    289 
    290   return signin_status.Pass();
    291 }
    292 
    293 } //  namespace signin_internals_util
    294