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