Home | History | Annotate | Download | only in webui
      1 // Copyright 2013 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/ui/webui/identity_internals_ui.h"
      6 
      7 #include <set>
      8 #include <string>
      9 
     10 #include "base/bind.h"
     11 #include "base/i18n/time_formatting.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/extensions/api/identity/identity_api.h"
     15 #include "chrome/browser/extensions/extension_service.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/common/url_constants.h"
     18 #include "chrome/grit/generated_resources.h"
     19 #include "content/public/browser/web_ui.h"
     20 #include "content/public/browser/web_ui_controller.h"
     21 #include "content/public/browser/web_ui_data_source.h"
     22 #include "content/public/browser/web_ui_message_handler.h"
     23 #include "extensions/browser/extension_system.h"
     24 #include "google_apis/gaia/gaia_auth_fetcher.h"
     25 #include "google_apis/gaia/gaia_constants.h"
     26 #include "grit/browser_resources.h"
     27 #include "ui/base/l10n/l10n_util.h"
     28 
     29 namespace {
     30 
     31 // Properties of the Javascript object representing a token.
     32 const char kExtensionId[] = "extensionId";
     33 const char kExtensionName[] = "extensionName";
     34 const char kScopes[] = "scopes";
     35 const char kStatus[] = "status";
     36 const char kTokenExpirationTime[] = "expirationTime";
     37 const char kAccessToken[] = "accessToken";
     38 
     39 // RevokeToken message parameter offsets.
     40 const int kRevokeTokenExtensionOffset = 0;
     41 const int kRevokeTokenTokenOffset = 1;
     42 
     43 class IdentityInternalsTokenRevoker;
     44 
     45 // Class acting as a controller of the chrome://identity-internals WebUI.
     46 class IdentityInternalsUIMessageHandler : public content::WebUIMessageHandler {
     47  public:
     48   IdentityInternalsUIMessageHandler();
     49   virtual ~IdentityInternalsUIMessageHandler();
     50 
     51   // Ensures that a proper clean up happens after a token is revoked. That
     52   // includes deleting the |token_revoker|, removing the token from Identity API
     53   // cache and updating the UI that the token is gone.
     54   void OnTokenRevokerDone(IdentityInternalsTokenRevoker* token_revoker);
     55 
     56   // WebUIMessageHandler implementation.
     57   virtual void RegisterMessages() OVERRIDE;
     58 
     59  private:
     60   // Gets the name of an extension referred to by |token_cache_key| as a string.
     61   const std::string GetExtensionName(
     62       const extensions::ExtensionTokenKey& token_cache_key);
     63 
     64   // Gets a list of scopes specified in |token_cache_key| and returns a pointer
     65   // to a ListValue containing the scopes. The caller gets ownership of the
     66   // returned object.
     67   base::ListValue* GetScopes(
     68       const extensions::ExtensionTokenKey& token_cache_key);
     69 
     70   // Gets a localized status of the access token in |token_cache_value|.
     71   const base::string16 GetStatus(
     72       const extensions::IdentityTokenCacheValue& token_cache_value);
     73 
     74   // Gets a string representation of an expiration time of the access token in
     75   // |token_cache_value|.
     76   const std::string GetExpirationTime(
     77       const extensions::IdentityTokenCacheValue& token_cache_value);
     78 
     79   // Converts a pair of |token_cache_key| and |token_cache_value| to a
     80   // DictionaryValue object with corresponding information in a localized and
     81   // readable form and returns a pointer to created object. Caller gets the
     82   // ownership of the returned object.
     83   base::DictionaryValue* GetInfoForToken(
     84       const extensions::ExtensionTokenKey& token_cache_key,
     85       const extensions::IdentityTokenCacheValue& token_cache_value);
     86 
     87   // Gets all of the tokens stored in IdentityAPI token cache and returns them
     88   // to the caller using Javascript callback function
     89   // |identity_internals.returnTokens()|.
     90   void GetInfoForAllTokens(const base::ListValue* args);
     91 
     92   // Initiates revoking of the token, based on the extension ID and token
     93   // passed as entries in the |args| list. Updates the caller of completion
     94   // using Javascript callback function |identity_internals.tokenRevokeDone()|.
     95   void RevokeToken(const base::ListValue* args);
     96 
     97   // A vector of token revokers that are currently revoking tokens.
     98   ScopedVector<IdentityInternalsTokenRevoker> token_revokers_;
     99 };
    100 
    101 // Handles the revoking of an access token and helps performing the clean up
    102 // after it is revoked by holding information about the access token and related
    103 // extension ID.
    104 class IdentityInternalsTokenRevoker : public GaiaAuthConsumer {
    105  public:
    106   // Revokes |access_token| from extension with |extension_id|.
    107   // |profile| is required for its request context. |consumer| will be
    108   // notified when revocation succeeds via |OnTokenRevokerDone()|.
    109   IdentityInternalsTokenRevoker(const std::string& extension_id,
    110                                 const std::string& access_token,
    111                                 Profile* profile,
    112                                 IdentityInternalsUIMessageHandler* consumer);
    113   virtual ~IdentityInternalsTokenRevoker();
    114 
    115   // Returns the access token being revoked.
    116   const std::string& access_token() const { return access_token_; }
    117 
    118   // Returns the ID of the extension the access token is related to.
    119   const std::string& extension_id() const { return extension_id_; }
    120 
    121   // GaiaAuthConsumer implementation.
    122   virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;
    123 
    124  private:
    125   // An object used to start a token revoke request.
    126   GaiaAuthFetcher fetcher_;
    127   // An ID of an extension the access token is related to.
    128   const std::string extension_id_;
    129   // The access token to revoke.
    130   const std::string access_token_;
    131   // An object that needs to be notified once the access token is revoked.
    132   IdentityInternalsUIMessageHandler* consumer_;  // weak.
    133 
    134   DISALLOW_COPY_AND_ASSIGN(IdentityInternalsTokenRevoker);
    135 };
    136 
    137 IdentityInternalsUIMessageHandler::IdentityInternalsUIMessageHandler() {}
    138 
    139 IdentityInternalsUIMessageHandler::~IdentityInternalsUIMessageHandler() {}
    140 
    141 void IdentityInternalsUIMessageHandler::OnTokenRevokerDone(
    142     IdentityInternalsTokenRevoker* token_revoker) {
    143   // Remove token from the cache.
    144   extensions::IdentityAPI::GetFactoryInstance()
    145       ->Get(Profile::FromWebUI(web_ui()))
    146       ->EraseCachedToken(token_revoker->extension_id(),
    147                          token_revoker->access_token());
    148 
    149   // Update view about the token being removed.
    150   base::ListValue result;
    151   result.AppendString(token_revoker->access_token());
    152   web_ui()->CallJavascriptFunction("identity_internals.tokenRevokeDone",
    153                                    result);
    154 
    155   // Erase the revoker.
    156   ScopedVector<IdentityInternalsTokenRevoker>::iterator iter =
    157       std::find(token_revokers_.begin(), token_revokers_.end(), token_revoker);
    158   DCHECK(iter != token_revokers_.end());
    159   token_revokers_.erase(iter);
    160 }
    161 
    162 const std::string IdentityInternalsUIMessageHandler::GetExtensionName(
    163     const extensions::ExtensionTokenKey& token_cache_key) {
    164   ExtensionService* extension_service = extensions::ExtensionSystem::Get(
    165       Profile::FromWebUI(web_ui()))->extension_service();
    166   const extensions::Extension* extension =
    167       extension_service->extensions()->GetByID(token_cache_key.extension_id);
    168   if (!extension)
    169     return std::string();
    170   return extension->name();
    171 }
    172 
    173 base::ListValue* IdentityInternalsUIMessageHandler::GetScopes(
    174     const extensions::ExtensionTokenKey& token_cache_key) {
    175   base::ListValue* scopes_value = new base::ListValue();
    176   for (std::set<std::string>::const_iterator
    177            iter = token_cache_key.scopes.begin();
    178        iter != token_cache_key.scopes.end(); ++iter) {
    179     scopes_value->AppendString(*iter);
    180   }
    181   return scopes_value;
    182 }
    183 
    184 const base::string16 IdentityInternalsUIMessageHandler::GetStatus(
    185     const extensions::IdentityTokenCacheValue& token_cache_value) {
    186   switch (token_cache_value.status()) {
    187     case extensions::IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
    188       // Fallthrough to NOT FOUND case, as ADVICE is short lived.
    189     case extensions::IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
    190       return l10n_util::GetStringUTF16(
    191           IDS_IDENTITY_INTERNALS_TOKEN_NOT_FOUND);
    192     case extensions::IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
    193       return l10n_util::GetStringUTF16(
    194           IDS_IDENTITY_INTERNALS_TOKEN_PRESENT);
    195   }
    196   NOTREACHED();
    197   return base::string16();
    198 }
    199 
    200 const std::string IdentityInternalsUIMessageHandler::GetExpirationTime(
    201     const extensions::IdentityTokenCacheValue& token_cache_value) {
    202   return base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
    203       token_cache_value.expiration_time()));
    204 }
    205 
    206 base::DictionaryValue* IdentityInternalsUIMessageHandler::GetInfoForToken(
    207     const extensions::ExtensionTokenKey& token_cache_key,
    208     const extensions::IdentityTokenCacheValue& token_cache_value) {
    209   base::DictionaryValue* token_data = new base::DictionaryValue();
    210   token_data->SetString(kExtensionId, token_cache_key.extension_id);
    211   token_data->SetString(kExtensionName, GetExtensionName(token_cache_key));
    212   token_data->Set(kScopes, GetScopes(token_cache_key));
    213   token_data->SetString(kStatus, GetStatus(token_cache_value));
    214   token_data->SetString(kAccessToken, token_cache_value.token());
    215   token_data->SetString(kTokenExpirationTime,
    216                         GetExpirationTime(token_cache_value));
    217   return token_data;
    218 }
    219 
    220 void IdentityInternalsUIMessageHandler::GetInfoForAllTokens(
    221     const base::ListValue* args) {
    222   base::ListValue results;
    223   extensions::IdentityAPI::CachedTokens tokens =
    224       extensions::IdentityAPI::GetFactoryInstance()
    225           ->Get(Profile::FromWebUI(web_ui()))
    226           ->GetAllCachedTokens();
    227   for (extensions::IdentityAPI::CachedTokens::const_iterator
    228            iter = tokens.begin(); iter != tokens.end(); ++iter) {
    229     results.Append(GetInfoForToken(iter->first, iter->second));
    230   }
    231 
    232   web_ui()->CallJavascriptFunction("identity_internals.returnTokens", results);
    233 }
    234 
    235 void IdentityInternalsUIMessageHandler::RegisterMessages() {
    236   web_ui()->RegisterMessageCallback("identityInternalsGetTokens",
    237       base::Bind(&IdentityInternalsUIMessageHandler::GetInfoForAllTokens,
    238                  base::Unretained(this)));
    239   web_ui()->RegisterMessageCallback("identityInternalsRevokeToken",
    240       base::Bind(&IdentityInternalsUIMessageHandler::RevokeToken,
    241                  base::Unretained(this)));
    242 }
    243 
    244 void IdentityInternalsUIMessageHandler::RevokeToken(
    245     const base::ListValue* args) {
    246   std::string extension_id;
    247   std::string access_token;
    248   args->GetString(kRevokeTokenExtensionOffset, &extension_id);
    249   args->GetString(kRevokeTokenTokenOffset, &access_token);
    250   token_revokers_.push_back(new IdentityInternalsTokenRevoker(
    251       extension_id, access_token, Profile::FromWebUI(web_ui()), this));
    252 }
    253 
    254 IdentityInternalsTokenRevoker::IdentityInternalsTokenRevoker(
    255     const std::string& extension_id,
    256     const std::string& access_token,
    257     Profile* profile,
    258     IdentityInternalsUIMessageHandler* consumer)
    259     : fetcher_(this, GaiaConstants::kChromeSource,
    260                profile->GetRequestContext()),
    261       extension_id_(extension_id),
    262       access_token_(access_token),
    263       consumer_(consumer) {
    264   DCHECK(consumer_);
    265   fetcher_.StartRevokeOAuth2Token(access_token);
    266 }
    267 
    268 IdentityInternalsTokenRevoker::~IdentityInternalsTokenRevoker() {}
    269 
    270 void IdentityInternalsTokenRevoker::OnOAuth2RevokeTokenCompleted() {
    271   consumer_->OnTokenRevokerDone(this);
    272 }
    273 
    274 }  // namespace
    275 
    276 IdentityInternalsUI::IdentityInternalsUI(content::WebUI* web_ui)
    277   : content::WebUIController(web_ui) {
    278   // chrome://identity-internals source.
    279   content::WebUIDataSource* html_source =
    280     content::WebUIDataSource::Create(chrome::kChromeUIIdentityInternalsHost);
    281   html_source->SetUseJsonJSFormatV2();
    282 
    283   // Localized strings
    284   html_source->AddLocalizedString("tokenCacheHeader",
    285       IDS_IDENTITY_INTERNALS_TOKEN_CACHE_TEXT);
    286   html_source->AddLocalizedString("accessToken",
    287       IDS_IDENTITY_INTERNALS_ACCESS_TOKEN);
    288   html_source->AddLocalizedString("extensionName",
    289       IDS_IDENTITY_INTERNALS_EXTENSION_NAME);
    290   html_source->AddLocalizedString("extensionId",
    291       IDS_IDENTITY_INTERNALS_EXTENSION_ID);
    292   html_source->AddLocalizedString("tokenStatus",
    293       IDS_IDENTITY_INTERNALS_TOKEN_STATUS);
    294   html_source->AddLocalizedString("expirationTime",
    295       IDS_IDENTITY_INTERNALS_EXPIRATION_TIME);
    296   html_source->AddLocalizedString("scopes",
    297       IDS_IDENTITY_INTERNALS_SCOPES);
    298   html_source->AddLocalizedString("revoke",
    299       IDS_IDENTITY_INTERNALS_REVOKE);
    300   html_source->SetJsonPath("strings.js");
    301 
    302   // Required resources
    303   html_source->AddResourcePath("identity_internals.css",
    304       IDR_IDENTITY_INTERNALS_CSS);
    305   html_source->AddResourcePath("identity_internals.js",
    306       IDR_IDENTITY_INTERNALS_JS);
    307   html_source->SetDefaultResource(IDR_IDENTITY_INTERNALS_HTML);
    308 
    309   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
    310 
    311   web_ui->AddMessageHandler(new IdentityInternalsUIMessageHandler());
    312 }
    313 
    314 IdentityInternalsUI::~IdentityInternalsUI() {}
    315