Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(459)

Side by Side Diff: chrome/browser/extensions/extension_permissions_api.cc

Issue 7432006: Add an experimental permissions API for extensions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/extensions/extension_permissions_api.h"
6
7 #include "base/json/json_writer.h"
8 #include "base/stringprintf.h"
9 #include "base/values.h"
10 #include "chrome/browser/extensions/extension_event_router.h"
11 #include "chrome/browser/extensions/extension_permissions_api_constants.h"
12 #include "chrome/browser/extensions/extension_prefs.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/chrome_notification_types.h"
16 #include "chrome/common/extensions/extension.h"
17 #include "chrome/common/extensions/extension_messages.h"
18 #include "chrome/common/extensions/extension_permission_set.h"
19 #include "chrome/common/extensions/url_pattern_set.h"
20 #include "content/common/notification_service.h"
21 #include "googleurl/src/gurl.h"
22
23
24 namespace keys = extension_permissions_module_constants;
25
26 namespace {
27
28 enum AutoConfirmForTest {
29 DO_NOT_SKIP = 0,
30 PROCEED,
31 ABORT
32 };
33 AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
34
35 DictionaryValue* CreatePermissionsValue(const ExtensionPermissionSet* set) {
36 DictionaryValue* value = new DictionaryValue();
37
38 // Generate the list of API permissions.
39 ListValue* apis = new ListValue();
40 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
41 for (ExtensionAPIPermissionSet::const_iterator i = set->apis().begin();
42 i != set->apis().end(); ++i)
43 apis->Append(Value::CreateStringValue(info->GetByID(*i)->name()));
44
45 // TODO(jstritar): Include hosts once the API supports them.
46
47 value->Set(keys::kApisKey, apis);
48 return value;
49 }
50
51 // Creates a new ExtensionPermissionSet from its |value| and passes ownership to
52 // the caller through |ptr|. Sets |bad_message| to true if the message is badly
53 // formed. Returns false if the method fails to unpack a permission set.
54 bool UnpackPermissionSet(DictionaryValue* value,
Mihai Parparita -not on Chrome 2011/07/20 22:03:43 Seems like CreatePermissionsValue and UnpackPermis
jstritar 2011/07/22 19:21:55 Done.
55 scoped_ptr<ExtensionPermissionSet>* ptr,
56 bool* bad_message,
57 std::string* error) {
58 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
59 ExtensionAPIPermissionSet apis;
60 if (value->HasKey(keys::kApisKey)) {
61 ListValue* api_list = NULL;
62 if (!value->GetList(keys::kApisKey, &api_list)) {
63 *bad_message = true;
64 return false;
65 }
66 for (size_t i = 0; i < api_list->GetSize(); ++i) {
67 std::string api_name;
68 if (!api_list->GetString(i, &api_name)) {
69 *bad_message = true;
70 return false;
71 }
72
73 ExtensionAPIPermission* permission = info->GetByName(api_name);
74 if (!permission) {
75 *error = base::StringPrintf(
76 keys::kUnknownPermissionError, api_name.c_str());
77 return false;
78 }
79 apis.insert(permission->id());
80 }
81 }
82
83 // Ignore host permissions for now.
84 URLPatternSet empty_set;
85 ptr->reset(new ExtensionPermissionSet(apis, empty_set, empty_set));
86 return true;
87 }
88
89 } // namespace
90
91 ExtensionPermissionsManager::ExtensionPermissionsManager(
92 ExtensionService* extension_service)
93 : extension_service_(extension_service) {
94 RegisterWhiteList();
95 }
96
97 ExtensionPermissionsManager::~ExtensionPermissionsManager() {
98 }
99
100 void ExtensionPermissionsManager::AddPermissions(
101 const Extension* extension, const ExtensionPermissionSet* permissions) {
102 const ExtensionPermissionSet* existing = extension->GetActivePermissions();
Mihai Parparita -not on Chrome 2011/07/21 21:18:13 I the type of existing should be scoped_refptr<Ext
jstritar 2011/07/22 19:21:55 Woops, nice catch. Done.
103 scoped_refptr<ExtensionPermissionSet> total(
104 ExtensionPermissionSet::CreateUnion(existing, permissions));
105 scoped_ptr<ExtensionPermissionSet> added(
106 ExtensionPermissionSet::CreateDifference(total.get(), existing));
107
108 extension_service_->UpdateActivePermissions(extension, total.get());
109
110 // Update the granted permissions so we don't auto-disable the extension.
111 if (extension->location() == Extension::INTERNAL)
Mihai Parparita -not on Chrome 2011/07/20 22:03:43 Not sure I understand the location check. Don't we
jstritar 2011/07/22 19:21:55 We only prompt the user to confirm permissions for
112 extension_service_->GrantPermissions(extension);
113
114 NotifyPermissionsUpdated(extension, total.get(), added.get(), ADDED);
115 }
116
117 void ExtensionPermissionsManager::RemovePermissions(
118 const Extension* extension, const ExtensionPermissionSet* permissions) {
119 const ExtensionPermissionSet* existing = extension->GetActivePermissions();
120 scoped_refptr<ExtensionPermissionSet> total(
121 ExtensionPermissionSet::CreateDifference(existing, permissions));
122 scoped_ptr<ExtensionPermissionSet> removed(
123 ExtensionPermissionSet::CreateDifference(existing, total.get()));
124
125 // We update the active permissions, and not the granted permissions, because
126 // the extension, not the user, removed the permissions. This allows the
127 // extension to add them again without prompting the user.
128 extension_service_->UpdateActivePermissions(extension, total.get());
129
130 NotifyPermissionsUpdated(extension, total.get(), removed.get(), REMOVED);
131 }
132
133 void ExtensionPermissionsManager::DispatchEvent(
134 const std::string& extension_id,
135 const char* event_name,
136 const ExtensionPermissionSet* permissions) {
137 if (!permissions || permissions->IsEmpty())
Mihai Parparita -not on Chrome 2011/07/20 22:03:43 Any reason why this check is here, and not earlier
jstritar 2011/07/22 19:21:55 Just a relic from some last minute shuffling. I've
138 return;
139
140 Profile* profile = extension_service_->profile();
141 if (profile && profile->GetExtensionEventRouter()) {
142 ListValue value;
143 value.Append(CreatePermissionsValue(permissions));
144 std::string json_value;
145 base::JSONWriter::Write(&value, false, &json_value);
146 profile->GetExtensionEventRouter()->DispatchEventToExtension(
147 extension_id, event_name, json_value, profile, GURL());
148 }
149 }
150
151 void ExtensionPermissionsManager::NotifyPermissionsUpdated(
152 const Extension* extension,
153 const ExtensionPermissionSet* active,
154 const ExtensionPermissionSet* changed,
155 EventType event_type) {
156 UpdatedExtensionPermissionsInfo::Reason reason;
157 const char* event_name = NULL;
158
159 if (event_type == REMOVED) {
160 reason = UpdatedExtensionPermissionsInfo::REMOVED;
161 event_name = keys::kOnRemoved;
162 } else {
163 CHECK_EQ(ADDED, event_type);
164 reason = UpdatedExtensionPermissionsInfo::ADDED;
165 event_name = keys::kOnAdded;
166 }
167
168 // Notify other APIs or interested parties.
169 UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
170 extension, changed, reason);
171 NotificationService::current()->Notify(
172 chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
173 Source<Profile>(extension_service_->profile()),
174 Details<UpdatedExtensionPermissionsInfo>(&info));
175
176 // Trigger the onAdded and onRemoved events in the extension.
177 DispatchEvent(extension->id(), event_name, changed);
178
179 // Send the new permissions to the renderers.
180 for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
181 !i.IsAtEnd(); i.Advance()) {
182 RenderProcessHost* host = i.GetCurrentValue();
183 if (extension_service_->profile()->IsSameProfile(host->profile()))
184 host->Send(new ExtensionMsg_UpdatePermissions(
185 extension->id(),
186 active->apis(),
187 active->explicit_hosts(),
188 active->scriptable_hosts()));
189 }
190 }
191
192 void ExtensionPermissionsManager::RegisterWhiteList() {
193 // TODO(jstritar): This could be a field on ExtensionAPIPermission.
194 ExtensionAPIPermissionSet api_white_list;
195 api_white_list.insert(ExtensionAPIPermission::kClipboardRead);
196 api_white_list.insert(ExtensionAPIPermission::kClipboardWrite);
197 api_white_list.insert(ExtensionAPIPermission::kNotification);
198 api_white_list.insert(ExtensionAPIPermission::kBookmark);
199 api_white_list.insert(ExtensionAPIPermission::kContextMenus);
200 api_white_list.insert(ExtensionAPIPermission::kCookie);
201 api_white_list.insert(ExtensionAPIPermission::kDebugger);
202 api_white_list.insert(ExtensionAPIPermission::kHistory);
203 api_white_list.insert(ExtensionAPIPermission::kIdle);
204 api_white_list.insert(ExtensionAPIPermission::kTab);
205 api_white_list.insert(ExtensionAPIPermission::kManagement);
206 api_white_list.insert(ExtensionAPIPermission::kBackground);
207 white_list_.reset(new ExtensionPermissionSet(
208 api_white_list, URLPatternSet(), URLPatternSet()));
209 }
210
211 bool ContainsPermissionsFunction::RunImpl() {
212 DictionaryValue* args = NULL;
213 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
214 std::string error;
215 if (!args)
216 return false;
217
218 scoped_ptr<ExtensionPermissionSet> permissions;
219 if (!UnpackPermissionSet(args, &permissions, &bad_message_, &error_))
220 return false;
221 CHECK(permissions.get());
222
223 result_.reset(Value::CreateBooleanValue(
224 GetExtension()->GetActivePermissions()->Contains(*permissions)));
225 return true;
226 }
227
228 bool GetAllPermissionsFunction::RunImpl() {
229 result_.reset(CreatePermissionsValue(
230 GetExtension()->GetActivePermissions()));
231 return true;
232 }
233
234 bool RemovePermissionsFunction::RunImpl() {
235 DictionaryValue* args = NULL;
236 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
237 if (!args)
238 return false;
239
240 scoped_ptr<ExtensionPermissionSet> permissions;
241 if (!UnpackPermissionSet(args, &permissions, &bad_message_, &error_))
242 return false;
243 CHECK(permissions.get());
244
245 const Extension* extension = GetExtension();
246 ExtensionPermissionsManager* perms_manager =
247 profile()->GetExtensionService()->permissions_manager();
248 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
249
250 // Make sure they're only trying to remove permissions supported by this API.
251 scoped_ptr<ExtensionPermissionSet> unsupported(
252 ExtensionPermissionSet::CreateDifference(
253 permissions.get(), &perms_manager->white_list()));
254 if (unsupported->apis().size()) {
255 std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
256 error_ = base::StringPrintf(keys::kNotWhiteListedError, api_name.c_str());
257 return false;
258 }
259
260 // Make sure we don't remove any required pemissions.
261 const ExtensionPermissionSet* required = extension->required_permission_set();
262 scoped_ptr<ExtensionPermissionSet> intersection(
263 ExtensionPermissionSet::CreateIntersection(permissions.get(), required));
264 if (!intersection->IsEmpty()) {
265 error_ = keys::kCantRemoveRequiredPermissionsError;
266 result_.reset(Value::CreateBooleanValue(false));
267 return false;
268 }
269
270 perms_manager->RemovePermissions(extension, permissions.get());
271 result_.reset(Value::CreateBooleanValue(true));
272 return true;
273 }
274
275 void RequestPermissionsFunction::SetAutoConfirmForTests(bool should_proceed) {
276 auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
277 }
278
279 bool RequestPermissionsFunction::RunImpl() {
280 DictionaryValue* args = NULL;
281 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
282 if (!args)
283 return false;
284
285 if (!UnpackPermissionSet(
286 args, &requested_permissions_, &bad_message_, &error_))
287 return false;
288 CHECK(requested_permissions_.get());
289
290 extension_ = GetExtension();
291 ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
292 ExtensionPermissionsManager* perms_manager =
293 profile()->GetExtensionService()->permissions_manager();
294 ExtensionPrefs* prefs = profile()->GetExtensionService()->extension_prefs();
295
296 // Make sure only white listed permissions have been requested.
297 scoped_ptr<ExtensionPermissionSet> unsupported(
298 ExtensionPermissionSet::CreateDifference(
299 requested_permissions_.get(), &perms_manager->white_list()));
300 if (unsupported->apis().size()) {
301 std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
302 error_ = base::StringPrintf(keys::kNotWhiteListedError, api_name.c_str());
303 return false;
304 }
305
306 // The requested permissions must be defined as optional in the manifest.
307 if (!extension_->optional_permission_set()->Contains(
308 *requested_permissions_)) {
309 error_ = keys::kNotInOptionalPermissionsError;
310 result_.reset(Value::CreateBooleanValue(false));
311 return false;
312 }
313
314 // We don't need to prompt the user if the requested permissions are a subset
315 // of the granted permissions set.
Mihai Parparita -not on Chrome 2011/07/20 22:03:43 Would it make sense to filter out the requested pe
jstritar 2011/07/22 19:21:55 Done.
316 const ExtensionPermissionSet* granted =
317 prefs->GetGrantedPermissions(extension_->id());
318 if (granted && granted->Contains(*requested_permissions_)) {
319 perms_manager->AddPermissions(extension_, requested_permissions_.get());
320 result_.reset(Value::CreateBooleanValue(true));
321 SendResponse(true);
322 return true;
323 }
324
325 // Balanced with Release() in InstallUIProceed() and InstallUIAbort().
326 AddRef();
327
328 // We don't need to show the prompt if there are no new warnings, or if
329 // we're skipping the confirmation UI.
330 if (auto_confirm_for_tests == PROCEED ||
331 requested_permissions_->GetWarningMessages().size() == 0) {
332 InstallUIProceed();
333 } else if (auto_confirm_for_tests == ABORT) {
334 // Pretend the user clicked cancel.
335 InstallUIAbort(true);
336 } else {
337 CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests);
338 install_ui_.reset(new ExtensionInstallUI(profile()));
339 install_ui_->ConfirmPermissions(
340 this, extension_, requested_permissions_.get());
341 }
342
343 return true;
344 }
345
346 void RequestPermissionsFunction::InstallUIProceed() {
347 ExtensionPermissionsManager* perms_manager =
348 profile()->GetExtensionService()->permissions_manager();
349
350 install_ui_.reset();
351 result_.reset(Value::CreateBooleanValue(true));
352 perms_manager->AddPermissions(extension_, requested_permissions_.get());
353
354 SendResponse(true);
355
356 Release();
357 }
358
359 void RequestPermissionsFunction::InstallUIAbort(bool user_initiated) {
360 install_ui_.reset();
361 result_.reset(Value::CreateBooleanValue(false));
362 requested_permissions_.reset();
363
364 SendResponse(true);
365 Release();
366 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698