[Extensions] Allow revokable permissions
Add back-end support for revokable extension permissions for optional
permissions, or, if the click-to-script feature is enabled, granted host
permissions.
BUG=532507
[email protected] (micro change in c/b/background/)
Review URL: https://codereview.chromium.org/1327523005
Cr-Commit-Position: refs/heads/master@{#349148}
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index 5014134e..9faa9342 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -20,6 +20,7 @@
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_messages.h"
+#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
@@ -122,14 +123,14 @@
void PermissionsUpdater::AddPermissions(
const Extension* extension, const PermissionSet* permissions) {
- scoped_refptr<const PermissionSet> existing(
+ scoped_refptr<const PermissionSet> active(
extension->permissions_data()->active_permissions());
scoped_refptr<PermissionSet> total(
- PermissionSet::CreateUnion(existing.get(), permissions));
+ PermissionSet::CreateUnion(active.get(), permissions));
scoped_refptr<PermissionSet> added(
- PermissionSet::CreateDifference(total.get(), existing.get()));
+ PermissionSet::CreateDifference(total.get(), active.get()));
- SetPermissions(extension, total, NULL);
+ SetPermissions(extension, total, nullptr);
// Update the granted permissions so we don't auto-disable the extension.
GrantActivePermissions(extension);
@@ -137,21 +138,95 @@
NotifyPermissionsUpdated(ADDED, extension, added.get());
}
-void PermissionsUpdater::RemovePermissions(
- const Extension* extension, const PermissionSet* permissions) {
- scoped_refptr<const PermissionSet> existing(
+void PermissionsUpdater::RemovePermissions(const Extension* extension,
+ const PermissionSet* to_remove,
+ RemoveType remove_type) {
+ // We should only be revoking revokable permissions.
+ CHECK(GetRevokablePermissions(extension)->Contains(*to_remove));
+
+ scoped_refptr<const PermissionSet> active(
+ extension->permissions_data()->active_permissions());
+ scoped_refptr<const PermissionSet> remaining(
+ PermissionSet::CreateDifference(active.get(), to_remove));
+
+ // Move any granted permissions that were in the withheld set back to the
+ // withheld set so they can be added back later.
+ // Any revoked permission that isn't from the optional permissions can only
+ // be a withheld permission.
+ scoped_refptr<const PermissionSet> removed_withheld =
+ PermissionSet::CreateDifference(
+ to_remove,
+ PermissionsParser::GetOptionalPermissions(extension).get());
+ scoped_refptr<const PermissionSet> withheld = PermissionSet::CreateUnion(
+ removed_withheld.get(),
+ extension->permissions_data()->withheld_permissions().get());
+
+ SetPermissions(extension, remaining, withheld);
+
+ // We might not want to revoke the granted permissions because the extension,
+ // not the user, removed the permissions. This allows the extension to add
+ // them again without prompting the user.
+ if (remove_type == REMOVE_HARD) {
+ ExtensionPrefs::Get(browser_context_)
+ ->RemoveGrantedPermissions(extension->id(), to_remove);
+ }
+
+ NotifyPermissionsUpdated(REMOVED, extension, to_remove);
+}
+
+void PermissionsUpdater::RemovePermissionsUnsafe(
+ const Extension* extension,
+ const PermissionSet* to_remove) {
+ scoped_refptr<const PermissionSet> active(
extension->permissions_data()->active_permissions());
scoped_refptr<PermissionSet> total(
- PermissionSet::CreateDifference(existing.get(), permissions));
- scoped_refptr<PermissionSet> removed(
- PermissionSet::CreateDifference(existing.get(), total.get()));
+ PermissionSet::CreateDifference(active.get(), to_remove));
+ // |successfully_removed| might not equal |to_remove| if |to_remove| contains
+ // permissions the extension didn't have.
+ scoped_refptr<PermissionSet> successfully_removed(
+ PermissionSet::CreateDifference(active.get(), 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.
- SetPermissions(extension, total, NULL);
+ SetPermissions(extension, total, nullptr);
+ NotifyPermissionsUpdated(REMOVED, extension, successfully_removed.get());
+}
- NotifyPermissionsUpdated(REMOVED, extension, removed.get());
+scoped_refptr<const PermissionSet> PermissionsUpdater::GetRevokablePermissions(
+ const Extension* extension) const {
+ // Optional permissions are revokable.
+ scoped_refptr<const PermissionSet> revokable_permissions =
+ PermissionsParser::GetOptionalPermissions(extension);
+ scoped_refptr<const PermissionSet> active_permissions =
+ extension->permissions_data()->active_permissions();
+ // If click-to-script is enabled, then any hosts that are granted, but not
+ // listed explicitly as a required permission, are also revokable.
+ if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
+ scoped_refptr<const PermissionSet> required_permissions =
+ PermissionsParser::GetRequiredPermissions(extension);
+ auto find_revokable_hosts = [](const URLPatternSet& active_hosts,
+ const URLPatternSet& required_hosts) {
+ URLPatternSet revokable_hosts;
+ for (const URLPattern& pattern : active_hosts) {
+ if (std::find(required_hosts.begin(), required_hosts.end(), pattern) ==
+ required_hosts.end()) {
+ revokable_hosts.AddPattern(pattern);
+ }
+ }
+ return revokable_hosts;
+ };
+ URLPatternSet revokable_explicit_hosts =
+ find_revokable_hosts(active_permissions->explicit_hosts(),
+ required_permissions->explicit_hosts());
+ URLPatternSet revokable_scriptable_hosts =
+ find_revokable_hosts(active_permissions->scriptable_hosts(),
+ required_permissions->scriptable_hosts());
+ scoped_refptr<const PermissionSet> revokable_host_permissions =
+ new PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
+ revokable_explicit_hosts, revokable_scriptable_hosts);
+ revokable_permissions = PermissionSet::CreateUnion(
+ revokable_permissions.get(), revokable_host_permissions.get());
+ }
+ return scoped_refptr<const PermissionSet>(PermissionSet::CreateIntersection(
+ active_permissions.get(), revokable_permissions.get()));
}
void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {