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 
     24 TokenInfo::TokenInfo(const std::string& truncated_token,
     25                      const std::string& status,
     26                      const std::string& time,
     27                      const int64& time_internal,
     28                      const std::string& service)
     29     : truncated_token(truncated_token),
     30       status(status),
     31       time(time),
     32       time_internal(time_internal),
     33       service(service) {
     34   // This should be a truncated and hashed token.
     35   DCHECK_LE(truncated_token.length(), kTruncateTokenStringLength);
     36 }
     37 
     38 TokenInfo::TokenInfo() {
     39 }
     40 
     41 TokenInfo::~TokenInfo() {
     42 }
     43 
     44 DictionaryValue* TokenInfo::ToValue() {
     45   scoped_ptr<DictionaryValue> token_info(new DictionaryValue());
     46   token_info->SetString("service", service);
     47   token_info->SetString("token", truncated_token);
     48   token_info->SetString("status", status);
     49   token_info->SetString("time", time);
     50 
     51   return token_info.release();
     52 }
     53 
     54 #define ENUM_CASE(x) case x: return (std::string(kSigninPrefPrefix) + #x)
     55 std::string SigninStatusFieldToString(UntimedSigninStatusField field) {
     56   switch (field) {
     57     ENUM_CASE(USERNAME);
     58     ENUM_CASE(SID);
     59     ENUM_CASE(LSID);
     60     case UNTIMED_FIELDS_END:
     61       NOTREACHED();
     62       return std::string();
     63   }
     64 
     65   NOTREACHED();
     66   return std::string();
     67 }
     68 
     69 std::string SigninStatusFieldToString(TimedSigninStatusField field) {
     70   switch (field) {
     71     ENUM_CASE(SIGNIN_TYPE);
     72     ENUM_CASE(CLIENT_LOGIN_STATUS);
     73     ENUM_CASE(OAUTH_LOGIN_STATUS);
     74     ENUM_CASE(GET_USER_INFO_STATUS);
     75     ENUM_CASE(UBER_TOKEN_STATUS);
     76     ENUM_CASE(MERGE_SESSION_STATUS);
     77     case TIMED_FIELDS_END:
     78       NOTREACHED();
     79       return std::string();
     80   }
     81 
     82   NOTREACHED();
     83   return std::string();
     84 }
     85 
     86 SigninStatus::SigninStatus()
     87     :untimed_signin_fields(UNTIMED_FIELDS_COUNT),
     88      timed_signin_fields(TIMED_FIELDS_COUNT) {
     89 }
     90 
     91 SigninStatus::~SigninStatus() {
     92 }
     93 
     94 std::string TokenPrefPath(const std::string& token_name) {
     95   return std::string(kTokenPrefPrefix) + token_name;
     96 }
     97 
     98 namespace {
     99 
    100 ListValue* AddSection(ListValue* parent_list, const std::string& title) {
    101   scoped_ptr<DictionaryValue> section(new DictionaryValue());
    102   ListValue* section_contents = new ListValue();
    103 
    104   section->SetString("title", title);
    105   section->Set("data", section_contents);
    106   parent_list->Append(section.release());
    107   return section_contents;
    108 }
    109 
    110 void AddSectionEntry(ListValue* section_list,
    111                      const std::string& field_name,
    112                      const std::string& field_val) {
    113   scoped_ptr<DictionaryValue> entry(new DictionaryValue());
    114   entry->SetString("label", field_name);
    115   entry->SetString("value", field_val);
    116   section_list->Append(entry.release());
    117 }
    118 
    119 
    120 // Returns a string describing the chrome version environment. Version format:
    121 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
    122 // If version information is unavailable, returns "invalid."
    123 std::string GetVersionString() {
    124   // Build a version string that matches MakeUserAgentForSyncApi with the
    125   // addition of channel info and proper OS names.
    126   chrome::VersionInfo chrome_version;
    127   if (!chrome_version.is_valid())
    128     return "invalid";
    129   // GetVersionStringModifier returns empty string for stable channel or
    130   // unofficial builds, the channel string otherwise. We want to have "-devel"
    131   // for unofficial builds only.
    132   std::string version_modifier =
    133       chrome::VersionInfo::GetVersionStringModifier();
    134   if (version_modifier.empty()) {
    135     if (chrome::VersionInfo::GetChannel() !=
    136             chrome::VersionInfo::CHANNEL_STABLE) {
    137       version_modifier = "-devel";
    138     }
    139   } else {
    140     version_modifier = " " + version_modifier;
    141   }
    142   return chrome_version.Name() + " " + chrome_version.OSType() + " " +
    143       chrome_version.Version() + " (" + chrome_version.LastChange() + ")" +
    144       version_modifier;
    145 }
    146 
    147 
    148 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
    149   switch (field) {
    150     case USERNAME:
    151       return "User Id";
    152     case LSID:
    153       return "Lsid (Hash)";
    154     case SID:
    155       return "Sid (Hash)";
    156     case UNTIMED_FIELDS_END:
    157       NOTREACHED();
    158       return std::string();
    159   }
    160   NOTREACHED();
    161   return std::string();
    162 }
    163 
    164 TimedSigninStatusValue SigninStatusFieldToLabel(
    165     TimedSigninStatusField field) {
    166   switch (field) {
    167     case SIGNIN_TYPE:
    168       return TimedSigninStatusValue("Type", "Time");
    169     case CLIENT_LOGIN_STATUS:
    170       return TimedSigninStatusValue("Last OnClientLogin Status",
    171                                     "Last OnClientLogin Time");
    172     case OAUTH_LOGIN_STATUS:
    173       return TimedSigninStatusValue("Last OnOAuthLogin Status",
    174                                     "Last OnOAuthLogin Time");
    175 
    176     case GET_USER_INFO_STATUS:
    177       return TimedSigninStatusValue("Last OnGetUserInfo Status",
    178                                     "Last OnGetUserInfo Time");
    179     case UBER_TOKEN_STATUS:
    180       return TimedSigninStatusValue("Last OnUberToken Status",
    181                                     "Last OnUberToken Time");
    182     case MERGE_SESSION_STATUS:
    183       return TimedSigninStatusValue("Last OnMergeSession Status",
    184                                     "Last OnMergeSession Time");
    185     case TIMED_FIELDS_END:
    186       NOTREACHED();
    187       return TimedSigninStatusValue("Error", std::string());
    188   }
    189   NOTREACHED();
    190   return TimedSigninStatusValue("Error", std::string());
    191 }
    192 
    193 } //  namespace
    194 
    195 scoped_ptr<DictionaryValue> SigninStatus::ToValue() {
    196   scoped_ptr<DictionaryValue> signin_status(new DictionaryValue());
    197   ListValue* signin_info = new ListValue();
    198   signin_status->Set("signin_info", signin_info);
    199 
    200   // A summary of signin related info first.
    201   ListValue* basic_info = AddSection(signin_info, "Basic Information");
    202   const std::string signin_status_string =
    203       untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty() ?
    204       "Not Signed In" : "Signed In";
    205   AddSectionEntry(basic_info, "Chrome Version", GetVersionString());
    206   AddSectionEntry(basic_info, "Signin Status", signin_status_string);
    207 
    208   // Only add username.  SID and LSID have moved to tokens section.
    209   const std::string field =
    210       SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
    211   AddSectionEntry(
    212       basic_info,
    213       field,
    214       untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
    215 
    216   // Time and status information of the possible sign in types.
    217   ListValue* detailed_info = AddSection(signin_info, "Last Signin Details");
    218   for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
    219     const std::string value_field =
    220         SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).first;
    221     const std::string time_field =
    222         SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).second;
    223 
    224     AddSectionEntry(detailed_info, value_field,
    225                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].first);
    226     AddSectionEntry(detailed_info, time_field,
    227                     timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
    228   }
    229 
    230   // Token information for all services.
    231   ListValue* token_info = new ListValue();
    232   ListValue* token_details = AddSection(token_info, "Token Details");
    233   signin_status->Set("token_info", token_info);
    234   for (std::map<std::string, TokenInfo>::iterator it = token_info_map.begin();
    235        it != token_info_map.end(); ++it) {
    236     DictionaryValue* token_info = it->second.ToValue();
    237     token_details->Append(token_info);
    238   }
    239 
    240   return signin_status.Pass();
    241 }
    242 
    243 // Gets the first few hex characters of the SHA256 hash of the passed in string.
    244 // These are enough to perform equality checks across a single users tokens,
    245 // while preventing outsiders from reverse-engineering the actual token from
    246 // the displayed value.
    247 // Note that for readability (in about:signin-internals), an empty string
    248 // is not hashed, but simply returned as an empty string.
    249 std::string GetTruncatedHash(const std::string& str) {
    250   if (str.empty())
    251     return str;
    252 
    253   // Since each character in the hash string generates two hex charaters
    254   // we only need half as many charaters in |hash_val| as hex characters
    255   // returned.
    256   const int kTruncateSize = kTruncateTokenStringLength / 2;
    257   char hash_val[kTruncateSize];
    258   crypto::SHA256HashString(str, &hash_val[0], kTruncateSize);
    259   return StringToLowerASCII(base::HexEncode(&hash_val[0], kTruncateSize));
    260 }
    261 
    262 } //  namespace signin_internals_util
    263