root/chrome/browser/ui/webui/identity_internals_ui.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. access_token
  2. extension_id
  3. OnTokenRevokerDone
  4. GetExtensionName
  5. GetScopes
  6. GetStatus
  7. GetExpirationTime
  8. GetInfoForToken
  9. GetInfoForAllTokens
  10. RegisterMessages
  11. RevokeToken
  12. consumer_
  13. OnOAuth2RevokeTokenCompleted

// Copyright 2013 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/ui/webui/identity_internals_ui.h"

#include <set>
#include <string>

#include "base/bind.h"
#include "base/i18n/time_formatting.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "extensions/browser/extension_system.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_constants.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

// Properties of the Javascript object representing a token.
const char kExtensionId[] = "extensionId";
const char kExtensionName[] = "extensionName";
const char kScopes[] = "scopes";
const char kStatus[] = "status";
const char kTokenExpirationTime[] = "expirationTime";
const char kAccessToken[] = "accessToken";

// RevokeToken message parameter offsets.
const int kRevokeTokenExtensionOffset = 0;
const int kRevokeTokenTokenOffset = 1;

class IdentityInternalsTokenRevoker;

// Class acting as a controller of the chrome://identity-internals WebUI.
class IdentityInternalsUIMessageHandler : public content::WebUIMessageHandler {
 public:
  IdentityInternalsUIMessageHandler();
  virtual ~IdentityInternalsUIMessageHandler();

  // Ensures that a proper clean up happens after a token is revoked. That
  // includes deleting the |token_revoker|, removing the token from Identity API
  // cache and updating the UI that the token is gone.
  void OnTokenRevokerDone(IdentityInternalsTokenRevoker* token_revoker);

  // WebUIMessageHandler implementation.
  virtual void RegisterMessages() OVERRIDE;

 private:
  // Gets the name of an extension referred to by |token_cache_key| as a string.
  const std::string GetExtensionName(
      const extensions::ExtensionTokenKey& token_cache_key);

  // Gets a list of scopes specified in |token_cache_key| and returns a pointer
  // to a ListValue containing the scopes. The caller gets ownership of the
  // returned object.
  base::ListValue* GetScopes(
      const extensions::ExtensionTokenKey& token_cache_key);

  // Gets a localized status of the access token in |token_cache_value|.
  const base::string16 GetStatus(
      const extensions::IdentityTokenCacheValue& token_cache_value);

  // Gets a string representation of an expiration time of the access token in
  // |token_cache_value|.
  const std::string GetExpirationTime(
      const extensions::IdentityTokenCacheValue& token_cache_value);

  // Converts a pair of |token_cache_key| and |token_cache_value| to a
  // DictionaryValue object with corresponding information in a localized and
  // readable form and returns a pointer to created object. Caller gets the
  // ownership of the returned object.
  base::DictionaryValue* GetInfoForToken(
      const extensions::ExtensionTokenKey& token_cache_key,
      const extensions::IdentityTokenCacheValue& token_cache_value);

  // Gets all of the tokens stored in IdentityAPI token cache and returns them
  // to the caller using Javascript callback function
  // |identity_internals.returnTokens()|.
  void GetInfoForAllTokens(const base::ListValue* args);

  // Initiates revoking of the token, based on the extension ID and token
  // passed as entries in the |args| list. Updates the caller of completion
  // using Javascript callback function |identity_internals.tokenRevokeDone()|.
  void RevokeToken(const base::ListValue* args);

  // A vector of token revokers that are currently revoking tokens.
  ScopedVector<IdentityInternalsTokenRevoker> token_revokers_;
};

// Handles the revoking of an access token and helps performing the clean up
// after it is revoked by holding information about the access token and related
// extension ID.
class IdentityInternalsTokenRevoker : public GaiaAuthConsumer {
 public:
  // Revokes |access_token| from extension with |extension_id|.
  // |profile| is required for its request context. |consumer| will be
  // notified when revocation succeeds via |OnTokenRevokerDone()|.
  IdentityInternalsTokenRevoker(const std::string& extension_id,
                                const std::string& access_token,
                                Profile* profile,
                                IdentityInternalsUIMessageHandler* consumer);
  virtual ~IdentityInternalsTokenRevoker();

  // Returns the access token being revoked.
  const std::string& access_token() const { return access_token_; }

  // Returns the ID of the extension the access token is related to.
  const std::string& extension_id() const { return extension_id_; }

  // GaiaAuthConsumer implementation.
  virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;

 private:
  // An object used to start a token revoke request.
  GaiaAuthFetcher fetcher_;
  // An ID of an extension the access token is related to.
  const std::string extension_id_;
  // The access token to revoke.
  const std::string access_token_;
  // An object that needs to be notified once the access token is revoked.
  IdentityInternalsUIMessageHandler* consumer_;  // weak.

  DISALLOW_COPY_AND_ASSIGN(IdentityInternalsTokenRevoker);
};

IdentityInternalsUIMessageHandler::IdentityInternalsUIMessageHandler() {}

IdentityInternalsUIMessageHandler::~IdentityInternalsUIMessageHandler() {}

void IdentityInternalsUIMessageHandler::OnTokenRevokerDone(
    IdentityInternalsTokenRevoker* token_revoker) {
  // Remove token from the cache.
  extensions::IdentityAPI::GetFactoryInstance()
      ->Get(Profile::FromWebUI(web_ui()))
      ->EraseCachedToken(token_revoker->extension_id(),
                         token_revoker->access_token());

  // Update view about the token being removed.
  base::ListValue result;
  result.AppendString(token_revoker->access_token());
  web_ui()->CallJavascriptFunction("identity_internals.tokenRevokeDone",
                                   result);

  // Erase the revoker.
  ScopedVector<IdentityInternalsTokenRevoker>::iterator iter =
      std::find(token_revokers_.begin(), token_revokers_.end(), token_revoker);
  DCHECK(iter != token_revokers_.end());
  token_revokers_.erase(iter);
}

const std::string IdentityInternalsUIMessageHandler::GetExtensionName(
    const extensions::ExtensionTokenKey& token_cache_key) {
  ExtensionService* extension_service = extensions::ExtensionSystem::Get(
      Profile::FromWebUI(web_ui()))->extension_service();
  const extensions::Extension* extension =
      extension_service->extensions()->GetByID(token_cache_key.extension_id);
  if (!extension)
    return std::string();
  return extension->name();
}

base::ListValue* IdentityInternalsUIMessageHandler::GetScopes(
    const extensions::ExtensionTokenKey& token_cache_key) {
  base::ListValue* scopes_value = new base::ListValue();
  for (std::set<std::string>::const_iterator
           iter = token_cache_key.scopes.begin();
       iter != token_cache_key.scopes.end(); ++iter) {
    scopes_value->AppendString(*iter);
  }
  return scopes_value;
}

const base::string16 IdentityInternalsUIMessageHandler::GetStatus(
    const extensions::IdentityTokenCacheValue& token_cache_value) {
  switch (token_cache_value.status()) {
    case extensions::IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
      // Fallthrough to NOT FOUND case, as ADVICE is short lived.
    case extensions::IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
      return l10n_util::GetStringUTF16(
          IDS_IDENTITY_INTERNALS_TOKEN_NOT_FOUND);
    case extensions::IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
      return l10n_util::GetStringUTF16(
          IDS_IDENTITY_INTERNALS_TOKEN_PRESENT);
  }
  NOTREACHED();
  return base::string16();
}

const std::string IdentityInternalsUIMessageHandler::GetExpirationTime(
    const extensions::IdentityTokenCacheValue& token_cache_value) {
  return base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
      token_cache_value.expiration_time()));
}

base::DictionaryValue* IdentityInternalsUIMessageHandler::GetInfoForToken(
    const extensions::ExtensionTokenKey& token_cache_key,
    const extensions::IdentityTokenCacheValue& token_cache_value) {
  base::DictionaryValue* token_data = new base::DictionaryValue();
  token_data->SetString(kExtensionId, token_cache_key.extension_id);
  token_data->SetString(kExtensionName, GetExtensionName(token_cache_key));
  token_data->Set(kScopes, GetScopes(token_cache_key));
  token_data->SetString(kStatus, GetStatus(token_cache_value));
  token_data->SetString(kAccessToken, token_cache_value.token());
  token_data->SetString(kTokenExpirationTime,
                        GetExpirationTime(token_cache_value));
  return token_data;
}

void IdentityInternalsUIMessageHandler::GetInfoForAllTokens(
    const base::ListValue* args) {
  base::ListValue results;
  extensions::IdentityAPI::CachedTokens tokens =
      extensions::IdentityAPI::GetFactoryInstance()
          ->Get(Profile::FromWebUI(web_ui()))
          ->GetAllCachedTokens();
  for (extensions::IdentityAPI::CachedTokens::const_iterator
           iter = tokens.begin(); iter != tokens.end(); ++iter) {
    results.Append(GetInfoForToken(iter->first, iter->second));
  }

  web_ui()->CallJavascriptFunction("identity_internals.returnTokens", results);
}

void IdentityInternalsUIMessageHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback("identityInternalsGetTokens",
      base::Bind(&IdentityInternalsUIMessageHandler::GetInfoForAllTokens,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("identityInternalsRevokeToken",
      base::Bind(&IdentityInternalsUIMessageHandler::RevokeToken,
                 base::Unretained(this)));
}

void IdentityInternalsUIMessageHandler::RevokeToken(
    const base::ListValue* args) {
  std::string extension_id;
  std::string access_token;
  args->GetString(kRevokeTokenExtensionOffset, &extension_id);
  args->GetString(kRevokeTokenTokenOffset, &access_token);
  token_revokers_.push_back(new IdentityInternalsTokenRevoker(
      extension_id, access_token, Profile::FromWebUI(web_ui()), this));
}

IdentityInternalsTokenRevoker::IdentityInternalsTokenRevoker(
    const std::string& extension_id,
    const std::string& access_token,
    Profile* profile,
    IdentityInternalsUIMessageHandler* consumer)
    : fetcher_(this, GaiaConstants::kChromeSource,
               profile->GetRequestContext()),
      extension_id_(extension_id),
      access_token_(access_token),
      consumer_(consumer) {
  DCHECK(consumer_);
  fetcher_.StartRevokeOAuth2Token(access_token);
}

IdentityInternalsTokenRevoker::~IdentityInternalsTokenRevoker() {}

void IdentityInternalsTokenRevoker::OnOAuth2RevokeTokenCompleted() {
  consumer_->OnTokenRevokerDone(this);
}

}  // namespace

IdentityInternalsUI::IdentityInternalsUI(content::WebUI* web_ui)
  : content::WebUIController(web_ui) {
  // chrome://identity-internals source.
  content::WebUIDataSource* html_source =
    content::WebUIDataSource::Create(chrome::kChromeUIIdentityInternalsHost);
  html_source->SetUseJsonJSFormatV2();

  // Localized strings
  html_source->AddLocalizedString("tokenCacheHeader",
      IDS_IDENTITY_INTERNALS_TOKEN_CACHE_TEXT);
  html_source->AddLocalizedString("accessToken",
      IDS_IDENTITY_INTERNALS_ACCESS_TOKEN);
  html_source->AddLocalizedString("extensionName",
      IDS_IDENTITY_INTERNALS_EXTENSION_NAME);
  html_source->AddLocalizedString("extensionId",
      IDS_IDENTITY_INTERNALS_EXTENSION_ID);
  html_source->AddLocalizedString("tokenStatus",
      IDS_IDENTITY_INTERNALS_TOKEN_STATUS);
  html_source->AddLocalizedString("expirationTime",
      IDS_IDENTITY_INTERNALS_EXPIRATION_TIME);
  html_source->AddLocalizedString("scopes",
      IDS_IDENTITY_INTERNALS_SCOPES);
  html_source->AddLocalizedString("revoke",
      IDS_IDENTITY_INTERNALS_REVOKE);
  html_source->SetJsonPath("strings.js");

  // Required resources
  html_source->AddResourcePath("identity_internals.css",
      IDR_IDENTITY_INTERNALS_CSS);
  html_source->AddResourcePath("identity_internals.js",
      IDR_IDENTITY_INTERNALS_JS);
  html_source->SetDefaultResource(IDR_IDENTITY_INTERNALS_HTML);

  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);

  web_ui->AddMessageHandler(new IdentityInternalsUIMessageHandler());
}

IdentityInternalsUI::~IdentityInternalsUI() {}

/* [<][>][^][v][top][bottom][index][help] */