Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |