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.
5 #include "chrome/browser/ui/webui/identity_internals_ui.h"
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 "extensions/browser/extension_system.h"
23 #include "google_apis/gaia/gaia_auth_fetcher.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "grit/browser_resources.h"
26 #include "grit/generated_resources.h"
27 #include "ui/base/l10n/l10n_util.h"
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";
39 // RevokeToken message parameter offsets.
40 const int kRevokeTokenExtensionOffset
= 0;
41 const int kRevokeTokenTokenOffset
= 1;
43 class IdentityInternalsTokenRevoker
;
45 // Class acting as a controller of the chrome://identity-internals WebUI.
46 class IdentityInternalsUIMessageHandler
: public content::WebUIMessageHandler
{
48 IdentityInternalsUIMessageHandler();
49 virtual ~IdentityInternalsUIMessageHandler();
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
);
56 // WebUIMessageHandler implementation.
57 virtual void RegisterMessages() OVERRIDE
;
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
);
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
67 base::ListValue
* GetScopes(
68 const extensions::ExtensionTokenKey
& token_cache_key
);
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
);
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
);
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
);
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
);
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
);
97 // A vector of token revokers that are currently revoking tokens.
98 ScopedVector
<IdentityInternalsTokenRevoker
> token_revokers_
;
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
104 class IdentityInternalsTokenRevoker
: public GaiaAuthConsumer
{
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
,
112 IdentityInternalsUIMessageHandler
* consumer
);
113 virtual ~IdentityInternalsTokenRevoker();
115 // Returns the access token being revoked.
116 const std::string
& access_token() const { return access_token_
; }
118 // Returns the ID of the extension the access token is related to.
119 const std::string
& extension_id() const { return extension_id_
; }
121 // GaiaAuthConsumer implementation.
122 virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE
;
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.
134 DISALLOW_COPY_AND_ASSIGN(IdentityInternalsTokenRevoker
);
137 IdentityInternalsUIMessageHandler::IdentityInternalsUIMessageHandler() {}
139 IdentityInternalsUIMessageHandler::~IdentityInternalsUIMessageHandler() {}
141 void IdentityInternalsUIMessageHandler::OnTokenRevokerDone(
142 IdentityInternalsTokenRevoker
* token_revoker
) {
143 // Remove token from the cache.
144 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
145 Profile::FromWebUI(web_ui()))->EraseCachedToken(
146 token_revoker
->extension_id(), token_revoker
->access_token());
148 // Update view about the token being removed.
149 base::ListValue result
;
150 result
.AppendString(token_revoker
->access_token());
151 web_ui()->CallJavascriptFunction("identity_internals.tokenRevokeDone",
154 // Erase the revoker.
155 ScopedVector
<IdentityInternalsTokenRevoker
>::iterator iter
=
156 std::find(token_revokers_
.begin(), token_revokers_
.end(), token_revoker
);
157 DCHECK(iter
!= token_revokers_
.end());
158 token_revokers_
.erase(iter
);
161 const std::string
IdentityInternalsUIMessageHandler::GetExtensionName(
162 const extensions::ExtensionTokenKey
& token_cache_key
) {
163 ExtensionService
* extension_service
= extensions::ExtensionSystem::Get(
164 Profile::FromWebUI(web_ui()))->extension_service();
165 const extensions::Extension
* extension
=
166 extension_service
->extensions()->GetByID(token_cache_key
.extension_id
);
168 return std::string();
169 return extension
->name();
172 base::ListValue
* IdentityInternalsUIMessageHandler::GetScopes(
173 const extensions::ExtensionTokenKey
& token_cache_key
) {
174 base::ListValue
* scopes_value
= new base::ListValue();
175 for (std::set
<std::string
>::const_iterator
176 iter
= token_cache_key
.scopes
.begin();
177 iter
!= token_cache_key
.scopes
.end(); ++iter
) {
178 scopes_value
->AppendString(*iter
);
183 const base::string16
IdentityInternalsUIMessageHandler::GetStatus(
184 const extensions::IdentityTokenCacheValue
& token_cache_value
) {
185 switch (token_cache_value
.status()) {
186 case extensions::IdentityTokenCacheValue::CACHE_STATUS_ADVICE
:
187 // Fallthrough to NOT FOUND case, as ADVICE is short lived.
188 case extensions::IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND
:
189 return l10n_util::GetStringUTF16(
190 IDS_IDENTITY_INTERNALS_TOKEN_NOT_FOUND
);
191 case extensions::IdentityTokenCacheValue::CACHE_STATUS_TOKEN
:
192 return l10n_util::GetStringUTF16(
193 IDS_IDENTITY_INTERNALS_TOKEN_PRESENT
);
196 return base::string16();
199 const std::string
IdentityInternalsUIMessageHandler::GetExpirationTime(
200 const extensions::IdentityTokenCacheValue
& token_cache_value
) {
201 return base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
202 token_cache_value
.expiration_time()));
205 base::DictionaryValue
* IdentityInternalsUIMessageHandler::GetInfoForToken(
206 const extensions::ExtensionTokenKey
& token_cache_key
,
207 const extensions::IdentityTokenCacheValue
& token_cache_value
) {
208 base::DictionaryValue
* token_data
= new base::DictionaryValue();
209 token_data
->SetString(kExtensionId
, token_cache_key
.extension_id
);
210 token_data
->SetString(kExtensionName
, GetExtensionName(token_cache_key
));
211 token_data
->Set(kScopes
, GetScopes(token_cache_key
));
212 token_data
->SetString(kStatus
, GetStatus(token_cache_value
));
213 token_data
->SetString(kAccessToken
, token_cache_value
.token());
214 token_data
->SetString(kTokenExpirationTime
,
215 GetExpirationTime(token_cache_value
));
219 void IdentityInternalsUIMessageHandler::GetInfoForAllTokens(
220 const base::ListValue
* args
) {
221 base::ListValue results
;
222 extensions::IdentityAPI::CachedTokens tokens
=
223 extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
224 Profile::FromWebUI(web_ui()))->GetAllCachedTokens();
225 for (extensions::IdentityAPI::CachedTokens::const_iterator
226 iter
= tokens
.begin(); iter
!= tokens
.end(); ++iter
) {
227 results
.Append(GetInfoForToken(iter
->first
, iter
->second
));
230 web_ui()->CallJavascriptFunction("identity_internals.returnTokens", results
);
233 void IdentityInternalsUIMessageHandler::RegisterMessages() {
234 web_ui()->RegisterMessageCallback("identityInternalsGetTokens",
235 base::Bind(&IdentityInternalsUIMessageHandler::GetInfoForAllTokens
,
236 base::Unretained(this)));
237 web_ui()->RegisterMessageCallback("identityInternalsRevokeToken",
238 base::Bind(&IdentityInternalsUIMessageHandler::RevokeToken
,
239 base::Unretained(this)));
242 void IdentityInternalsUIMessageHandler::RevokeToken(
243 const base::ListValue
* args
) {
244 std::string extension_id
;
245 std::string access_token
;
246 args
->GetString(kRevokeTokenExtensionOffset
, &extension_id
);
247 args
->GetString(kRevokeTokenTokenOffset
, &access_token
);
248 token_revokers_
.push_back(new IdentityInternalsTokenRevoker(
249 extension_id
, access_token
, Profile::FromWebUI(web_ui()), this));
252 IdentityInternalsTokenRevoker::IdentityInternalsTokenRevoker(
253 const std::string
& extension_id
,
254 const std::string
& access_token
,
256 IdentityInternalsUIMessageHandler
* consumer
)
257 : fetcher_(this, GaiaConstants::kChromeSource
,
258 profile
->GetRequestContext()),
259 extension_id_(extension_id
),
260 access_token_(access_token
),
261 consumer_(consumer
) {
263 fetcher_
.StartRevokeOAuth2Token(access_token
);
266 IdentityInternalsTokenRevoker::~IdentityInternalsTokenRevoker() {}
268 void IdentityInternalsTokenRevoker::OnOAuth2RevokeTokenCompleted() {
269 consumer_
->OnTokenRevokerDone(this);
274 IdentityInternalsUI::IdentityInternalsUI(content::WebUI
* web_ui
)
275 : content::WebUIController(web_ui
) {
276 // chrome://identity-internals source.
277 content::WebUIDataSource
* html_source
=
278 content::WebUIDataSource::Create(chrome::kChromeUIIdentityInternalsHost
);
279 html_source
->SetUseJsonJSFormatV2();
282 html_source
->AddLocalizedString("tokenCacheHeader",
283 IDS_IDENTITY_INTERNALS_TOKEN_CACHE_TEXT
);
284 html_source
->AddLocalizedString("accessToken",
285 IDS_IDENTITY_INTERNALS_ACCESS_TOKEN
);
286 html_source
->AddLocalizedString("extensionName",
287 IDS_IDENTITY_INTERNALS_EXTENSION_NAME
);
288 html_source
->AddLocalizedString("extensionId",
289 IDS_IDENTITY_INTERNALS_EXTENSION_ID
);
290 html_source
->AddLocalizedString("tokenStatus",
291 IDS_IDENTITY_INTERNALS_TOKEN_STATUS
);
292 html_source
->AddLocalizedString("expirationTime",
293 IDS_IDENTITY_INTERNALS_EXPIRATION_TIME
);
294 html_source
->AddLocalizedString("scopes",
295 IDS_IDENTITY_INTERNALS_SCOPES
);
296 html_source
->AddLocalizedString("revoke",
297 IDS_IDENTITY_INTERNALS_REVOKE
);
298 html_source
->SetJsonPath("strings.js");
300 // Required resources
301 html_source
->AddResourcePath("identity_internals.css",
302 IDR_IDENTITY_INTERNALS_CSS
);
303 html_source
->AddResourcePath("identity_internals.js",
304 IDR_IDENTITY_INTERNALS_JS
);
305 html_source
->SetDefaultResource(IDR_IDENTITY_INTERNALS_HTML
);
307 content::WebUIDataSource::Add(Profile::FromWebUI(web_ui
), html_source
);
309 web_ui
->AddMessageHandler(new IdentityInternalsUIMessageHandler());
312 IdentityInternalsUI::~IdentityInternalsUI() {}