| Index: chrome/browser/extensions/extension_permissions_api.cc
|
| diff --git a/chrome/browser/extensions/extension_permissions_api.cc b/chrome/browser/extensions/extension_permissions_api.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..87a413b0e2137e71f5560cc150e60ddb50bf787a
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/extension_permissions_api.cc
|
| @@ -0,0 +1,378 @@
|
| +// Copyright (c) 2011 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/extensions/extension_permissions_api.h"
|
| +
|
| +#include "base/json/json_writer.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/values.h"
|
| +#include "chrome/browser/extensions/extension_event_router.h"
|
| +#include "chrome/browser/extensions/extension_permissions_api_constants.h"
|
| +#include "chrome/browser/extensions/extension_prefs.h"
|
| +#include "chrome/browser/extensions/extension_service.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/common/chrome_notification_types.h"
|
| +#include "chrome/common/extensions/extension.h"
|
| +#include "chrome/common/extensions/extension_messages.h"
|
| +#include "chrome/common/extensions/extension_permission_set.h"
|
| +#include "chrome/common/extensions/url_pattern_set.h"
|
| +#include "content/common/notification_service.h"
|
| +#include "googleurl/src/gurl.h"
|
| +
|
| +
|
| +namespace keys = extension_permissions_module_constants;
|
| +
|
| +namespace {
|
| +
|
| +enum AutoConfirmForTest {
|
| + DO_NOT_SKIP = 0,
|
| + PROCEED,
|
| + ABORT
|
| +};
|
| +AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
|
| +
|
| +DictionaryValue* PackPermissionsToValue(const ExtensionPermissionSet* set) {
|
| + DictionaryValue* value = new DictionaryValue();
|
| +
|
| + // Generate the list of API permissions.
|
| + ListValue* apis = new ListValue();
|
| + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
|
| + for (ExtensionAPIPermissionSet::const_iterator i = set->apis().begin();
|
| + i != set->apis().end(); ++i)
|
| + apis->Append(Value::CreateStringValue(info->GetByID(*i)->name()));
|
| +
|
| + // TODO(jstritar): Include hosts once the API supports them. At that point,
|
| + // we could also shared this code with ExtensionPermissionSet methods in
|
| + // ExtensionPrefs.
|
| +
|
| + value->Set(keys::kApisKey, apis);
|
| + return value;
|
| +}
|
| +
|
| +// Creates a new ExtensionPermissionSet from its |value| and passes ownership to
|
| +// the caller through |ptr|. Sets |bad_message| to true if the message is badly
|
| +// formed. Returns false if the method fails to unpack a permission set.
|
| +bool UnpackPermissionsFromValue(DictionaryValue* value,
|
| + scoped_refptr<ExtensionPermissionSet>* ptr,
|
| + bool* bad_message,
|
| + std::string* error) {
|
| + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
|
| + ExtensionAPIPermissionSet apis;
|
| + if (value->HasKey(keys::kApisKey)) {
|
| + ListValue* api_list = NULL;
|
| + if (!value->GetList(keys::kApisKey, &api_list)) {
|
| + *bad_message = true;
|
| + return false;
|
| + }
|
| + for (size_t i = 0; i < api_list->GetSize(); ++i) {
|
| + std::string api_name;
|
| + if (!api_list->GetString(i, &api_name)) {
|
| + *bad_message = true;
|
| + return false;
|
| + }
|
| +
|
| + ExtensionAPIPermission* permission = info->GetByName(api_name);
|
| + if (!permission) {
|
| + *error = base::StringPrintf(
|
| + keys::kUnknownPermissionError, api_name.c_str());
|
| + return false;
|
| + }
|
| + apis.insert(permission->id());
|
| + }
|
| + }
|
| +
|
| + // Ignore host permissions for now.
|
| + URLPatternSet empty_set;
|
| + *ptr = new ExtensionPermissionSet(apis, empty_set, empty_set);
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +ExtensionPermissionsManager::ExtensionPermissionsManager(
|
| + ExtensionService* extension_service)
|
| + : extension_service_(extension_service) {
|
| + RegisterWhitelist();
|
| +}
|
| +
|
| +ExtensionPermissionsManager::~ExtensionPermissionsManager() {
|
| +}
|
| +
|
| +void ExtensionPermissionsManager::AddPermissions(
|
| + const Extension* extension, const ExtensionPermissionSet* permissions) {
|
| + scoped_refptr<const ExtensionPermissionSet> existing(
|
| + extension->GetActivePermissions());
|
| + scoped_refptr<ExtensionPermissionSet> total(
|
| + ExtensionPermissionSet::CreateUnion(existing, permissions));
|
| + scoped_refptr<ExtensionPermissionSet> added(
|
| + ExtensionPermissionSet::CreateDifference(total.get(), existing));
|
| +
|
| + extension_service_->UpdateActivePermissions(extension, total.get());
|
| +
|
| + // Update the granted permissions so we don't auto-disable the extension.
|
| + extension_service_->GrantPermissions(extension);
|
| +
|
| + NotifyPermissionsUpdated(extension, total.get(), added.get(), ADDED);
|
| +}
|
| +
|
| +void ExtensionPermissionsManager::RemovePermissions(
|
| + const Extension* extension, const ExtensionPermissionSet* permissions) {
|
| + scoped_refptr<const ExtensionPermissionSet> existing(
|
| + extension->GetActivePermissions());
|
| + scoped_refptr<ExtensionPermissionSet> total(
|
| + ExtensionPermissionSet::CreateDifference(existing, permissions));
|
| + scoped_refptr<ExtensionPermissionSet> removed(
|
| + ExtensionPermissionSet::CreateDifference(existing, total.get()));
|
| +
|
| + // We update the active permissions, and not the granted permissions, because
|
| + // the extension, not the user, removed the permissions. This allows the
|
| + // extension to add them again without prompting the user.
|
| + extension_service_->UpdateActivePermissions(extension, total.get());
|
| +
|
| + NotifyPermissionsUpdated(extension, total.get(), removed.get(), REMOVED);
|
| +}
|
| +
|
| +void ExtensionPermissionsManager::DispatchEvent(
|
| + const std::string& extension_id,
|
| + const char* event_name,
|
| + const ExtensionPermissionSet* changed_permissions) {
|
| + Profile* profile = extension_service_->profile();
|
| + if (profile && profile->GetExtensionEventRouter()) {
|
| + ListValue value;
|
| + value.Append(PackPermissionsToValue(changed_permissions));
|
| + std::string json_value;
|
| + base::JSONWriter::Write(&value, false, &json_value);
|
| + profile->GetExtensionEventRouter()->DispatchEventToExtension(
|
| + extension_id, event_name, json_value, profile, GURL());
|
| + }
|
| +}
|
| +
|
| +void ExtensionPermissionsManager::NotifyPermissionsUpdated(
|
| + const Extension* extension,
|
| + const ExtensionPermissionSet* active,
|
| + const ExtensionPermissionSet* changed,
|
| + EventType event_type) {
|
| + if (!changed || changed->IsEmpty())
|
| + return;
|
| +
|
| + UpdatedExtensionPermissionsInfo::Reason reason;
|
| + const char* event_name = NULL;
|
| +
|
| + if (event_type == REMOVED) {
|
| + reason = UpdatedExtensionPermissionsInfo::REMOVED;
|
| + event_name = keys::kOnRemoved;
|
| + } else {
|
| + CHECK_EQ(ADDED, event_type);
|
| + reason = UpdatedExtensionPermissionsInfo::ADDED;
|
| + event_name = keys::kOnAdded;
|
| + }
|
| +
|
| + // Notify other APIs or interested parties.
|
| + UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
|
| + extension, changed, reason);
|
| + NotificationService::current()->Notify(
|
| + chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
|
| + Source<Profile>(extension_service_->profile()),
|
| + Details<UpdatedExtensionPermissionsInfo>(&info));
|
| +
|
| + // Trigger the onAdded and onRemoved events in the extension.
|
| + DispatchEvent(extension->id(), event_name, changed);
|
| +
|
| + // Send the new permissions to the renderers.
|
| + for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
|
| + !i.IsAtEnd(); i.Advance()) {
|
| + RenderProcessHost* host = i.GetCurrentValue();
|
| + if (extension_service_->profile()->IsSameProfile(host->profile()))
|
| + host->Send(new ExtensionMsg_UpdatePermissions(
|
| + extension->id(),
|
| + active->apis(),
|
| + active->explicit_hosts(),
|
| + active->scriptable_hosts()));
|
| + }
|
| +}
|
| +
|
| +void ExtensionPermissionsManager::RegisterWhitelist() {
|
| + // TODO(jstritar): This could be a field on ExtensionAPIPermission.
|
| + ExtensionAPIPermissionSet api_whitelist;
|
| + api_whitelist.insert(ExtensionAPIPermission::kClipboardRead);
|
| + api_whitelist.insert(ExtensionAPIPermission::kClipboardWrite);
|
| + api_whitelist.insert(ExtensionAPIPermission::kNotification);
|
| + api_whitelist.insert(ExtensionAPIPermission::kBookmark);
|
| + api_whitelist.insert(ExtensionAPIPermission::kContextMenus);
|
| + api_whitelist.insert(ExtensionAPIPermission::kCookie);
|
| + api_whitelist.insert(ExtensionAPIPermission::kDebugger);
|
| + api_whitelist.insert(ExtensionAPIPermission::kHistory);
|
| + api_whitelist.insert(ExtensionAPIPermission::kIdle);
|
| + api_whitelist.insert(ExtensionAPIPermission::kTab);
|
| + api_whitelist.insert(ExtensionAPIPermission::kManagement);
|
| + api_whitelist.insert(ExtensionAPIPermission::kBackground);
|
| + whitelist_ = new ExtensionPermissionSet(
|
| + api_whitelist, URLPatternSet(), URLPatternSet());
|
| +}
|
| +
|
| +bool ContainsPermissionsFunction::RunImpl() {
|
| + DictionaryValue* args = NULL;
|
| + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
|
| + std::string error;
|
| + if (!args)
|
| + return false;
|
| +
|
| + scoped_refptr<ExtensionPermissionSet> permissions;
|
| + if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_))
|
| + return false;
|
| + CHECK(permissions.get());
|
| +
|
| + result_.reset(Value::CreateBooleanValue(
|
| + GetExtension()->GetActivePermissions()->Contains(*permissions)));
|
| + return true;
|
| +}
|
| +
|
| +bool GetAllPermissionsFunction::RunImpl() {
|
| + result_.reset(PackPermissionsToValue(
|
| + GetExtension()->GetActivePermissions()));
|
| + return true;
|
| +}
|
| +
|
| +bool RemovePermissionsFunction::RunImpl() {
|
| + DictionaryValue* args = NULL;
|
| + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
|
| + if (!args)
|
| + return false;
|
| +
|
| + scoped_refptr<ExtensionPermissionSet> permissions;
|
| + if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_))
|
| + return false;
|
| + CHECK(permissions.get());
|
| +
|
| + const Extension* extension = GetExtension();
|
| + ExtensionPermissionsManager* perms_manager =
|
| + profile()->GetExtensionService()->permissions_manager();
|
| + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
|
| +
|
| + // Make sure they're only trying to remove permissions supported by this API.
|
| + scoped_refptr<ExtensionPermissionSet> unsupported(
|
| + ExtensionPermissionSet::CreateDifference(
|
| + permissions.get(), &perms_manager->whitelist()));
|
| + if (unsupported->apis().size()) {
|
| + std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
|
| + error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str());
|
| + return false;
|
| + }
|
| +
|
| + // Make sure we don't remove any required pemissions.
|
| + const ExtensionPermissionSet* required = extension->required_permission_set();
|
| + scoped_refptr<ExtensionPermissionSet> intersection(
|
| + ExtensionPermissionSet::CreateIntersection(permissions.get(), required));
|
| + if (!intersection->IsEmpty()) {
|
| + error_ = keys::kCantRemoveRequiredPermissionsError;
|
| + result_.reset(Value::CreateBooleanValue(false));
|
| + return false;
|
| + }
|
| +
|
| + perms_manager->RemovePermissions(extension, permissions.get());
|
| + result_.reset(Value::CreateBooleanValue(true));
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +void RequestPermissionsFunction::SetAutoConfirmForTests(bool should_proceed) {
|
| + auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
|
| +}
|
| +
|
| +RequestPermissionsFunction::RequestPermissionsFunction() {}
|
| +RequestPermissionsFunction::~RequestPermissionsFunction() {}
|
| +
|
| +bool RequestPermissionsFunction::RunImpl() {
|
| + DictionaryValue* args = NULL;
|
| + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
|
| + if (!args)
|
| + return false;
|
| +
|
| + if (!UnpackPermissionsFromValue(
|
| + args, &requested_permissions_, &bad_message_, &error_))
|
| + return false;
|
| + CHECK(requested_permissions_.get());
|
| +
|
| + extension_ = GetExtension();
|
| + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
|
| + ExtensionPermissionsManager* perms_manager =
|
| + profile()->GetExtensionService()->permissions_manager();
|
| + ExtensionPrefs* prefs = profile()->GetExtensionService()->extension_prefs();
|
| +
|
| + // Make sure only white listed permissions have been requested.
|
| + scoped_refptr<ExtensionPermissionSet> unsupported(
|
| + ExtensionPermissionSet::CreateDifference(
|
| + requested_permissions_.get(), &perms_manager->whitelist()));
|
| + if (unsupported->apis().size()) {
|
| + std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
|
| + error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str());
|
| + return false;
|
| + }
|
| +
|
| + // The requested permissions must be defined as optional in the manifest.
|
| + if (!extension_->optional_permission_set()->Contains(
|
| + *requested_permissions_)) {
|
| + error_ = keys::kNotInOptionalPermissionsError;
|
| + result_.reset(Value::CreateBooleanValue(false));
|
| + return false;
|
| + }
|
| +
|
| + // We don't need to prompt the user if the requested permissions are a subset
|
| + // of the granted permissions set.
|
| + const ExtensionPermissionSet* granted =
|
| + prefs->GetGrantedPermissions(extension_->id());
|
| + if (granted && granted->Contains(*requested_permissions_)) {
|
| + perms_manager->AddPermissions(extension_, requested_permissions_.get());
|
| + result_.reset(Value::CreateBooleanValue(true));
|
| + SendResponse(true);
|
| + return true;
|
| + }
|
| +
|
| + // Filter out the granted permissions so we only prompt for new ones.
|
| + requested_permissions_ = ExtensionPermissionSet::CreateDifference(
|
| + requested_permissions_.get(), granted);
|
| +
|
| + // Balanced with Release() in InstallUIProceed() and InstallUIAbort().
|
| + AddRef();
|
| +
|
| + // We don't need to show the prompt if there are no new warnings, or if
|
| + // we're skipping the confirmation UI. All extension types but INTERNAL
|
| + // are allowed to silently increase their permission level.
|
| + if (auto_confirm_for_tests == PROCEED ||
|
| + requested_permissions_->GetWarningMessages().size() == 0) {
|
| + InstallUIProceed();
|
| + } else if (auto_confirm_for_tests == ABORT) {
|
| + // Pretend the user clicked cancel.
|
| + InstallUIAbort(true);
|
| + } else {
|
| + CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests);
|
| + install_ui_.reset(new ExtensionInstallUI(profile()));
|
| + install_ui_->ConfirmPermissions(
|
| + this, extension_, requested_permissions_.get());
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void RequestPermissionsFunction::InstallUIProceed() {
|
| + ExtensionPermissionsManager* perms_manager =
|
| + profile()->GetExtensionService()->permissions_manager();
|
| +
|
| + install_ui_.reset();
|
| + result_.reset(Value::CreateBooleanValue(true));
|
| + perms_manager->AddPermissions(extension_, requested_permissions_.get());
|
| +
|
| + SendResponse(true);
|
| +
|
| + Release();
|
| +}
|
| +
|
| +void RequestPermissionsFunction::InstallUIAbort(bool user_initiated) {
|
| + install_ui_.reset();
|
| + result_.reset(Value::CreateBooleanValue(false));
|
| + requested_permissions_ = NULL;
|
| +
|
| + SendResponse(true);
|
| + Release();
|
| +}
|
|
|