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