| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/permissions_updater.h" |
| |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/no_destructor.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h" |
| #include "chrome/browser/extensions/extension_management.h" |
| #include "chrome/browser/extensions/extension_system_factory.h" |
| #include "chrome/browser/extensions/scripting_permissions_modifier.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/extensions/api/permissions.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" |
| #include "components/keyed_service/core/keyed_service_shutdown_notifier.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/url_constants.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/event_router_factory.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_util.h" |
| #include "extensions/browser/network_permissions_updater.h" |
| #include "extensions/browser/permissions_manager.h" |
| #include "extensions/browser/renderer_startup_helper.h" |
| #include "extensions/common/cors_util.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_features.h" |
| #include "extensions/common/extension_messages.h" |
| #include "extensions/common/manifest_handlers/permissions_parser.h" |
| #include "extensions/common/mojom/permission_set.mojom.h" |
| #include "extensions/common/mojom/renderer.mojom.h" |
| #include "extensions/common/permissions/permission_set.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| |
| using content::RenderProcessHost; |
| using extensions::permissions_api_helpers::PackPermissionSet; |
| |
| namespace extensions { |
| |
| namespace permissions = api::permissions; |
| |
| namespace { |
| |
| // A helper class to watch profile lifetime. |
| class PermissionsUpdaterShutdownNotifierFactory |
| : public BrowserContextKeyedServiceShutdownNotifierFactory { |
| public: |
| PermissionsUpdaterShutdownNotifierFactory( |
| const PermissionsUpdaterShutdownNotifierFactory&) = delete; |
| PermissionsUpdaterShutdownNotifierFactory& operator=( |
| const PermissionsUpdaterShutdownNotifierFactory&) = delete; |
| |
| static PermissionsUpdaterShutdownNotifierFactory* GetInstance() { |
| static base::NoDestructor<PermissionsUpdaterShutdownNotifierFactory> |
| factory; |
| return factory.get(); |
| } |
| |
| private: |
| friend class base::NoDestructor<PermissionsUpdaterShutdownNotifierFactory>; |
| |
| PermissionsUpdaterShutdownNotifierFactory() |
| : BrowserContextKeyedServiceShutdownNotifierFactory( |
| "PermissionsUpdaterShutdownFactory") { |
| DependsOn(EventRouterFactory::GetInstance()); |
| DependsOn(ExtensionSystemFactory::GetInstance()); |
| } |
| ~PermissionsUpdaterShutdownNotifierFactory() override {} |
| }; |
| |
| // Returns an URLPatternSet containing the sites that the user has indicated |
| // extensions are always allowed to run on. |
| URLPatternSet GetUserPermittedPatternSet( |
| content::BrowserContext& browser_context) { |
| PermissionsManager* permissions_manager = |
| PermissionsManager::Get(&browser_context); |
| URLPatternSet user_permitted_sites; |
| for (const url::Origin& origin : |
| permissions_manager->GetUserPermissionsSettings().permitted_sites) { |
| user_permitted_sites.AddOrigin(Extension::kValidHostPermissionSchemes, |
| origin); |
| } |
| |
| return user_permitted_sites; |
| } |
| |
| } // namespace |
| |
| // A helper class to asynchronously dispatch the event to notify policy host |
| // restrictions or permissions once they have been updated. This will fire the |
| // event if and only if the BrowserContext is still valid. |
| // This class manages its own lifetime and deletes itself when either the |
| // permissions updated event is fired, or the BrowserContext is shut down |
| // (whichever happens first). |
| // TODO(devlin): After having extracted much of this into |
| // NetworkPermissionsUpdater, this class is a glorified watcher for the |
| // profile lifetime (since it depends on things like EventRouter). This might |
| // be able to be replaced with a simple check if the profile is still valid in |
| // a free function. |
| class PermissionsUpdater::NetworkPermissionsUpdateHelper { |
| public: |
| NetworkPermissionsUpdateHelper(const NetworkPermissionsUpdateHelper&) = |
| delete; |
| NetworkPermissionsUpdateHelper& operator=( |
| const NetworkPermissionsUpdateHelper&) = delete; |
| |
| static void UpdatePermissions(content::BrowserContext* browser_context, |
| EventType event_type, |
| scoped_refptr<const Extension> extension, |
| const PermissionSet& changed, |
| base::OnceClosure completion_callback); |
| |
| static void UpdateDefaultPolicyHostRestrictions( |
| content::BrowserContext* browser_context, |
| const URLPatternSet& default_runtime_blocked_hosts, |
| const URLPatternSet& default_runtime_allowed_hosts); |
| |
| private: |
| // This class manages its own lifetime. |
| NetworkPermissionsUpdateHelper(content::BrowserContext* browser_context, |
| base::OnceClosure dispatch_event); |
| ~NetworkPermissionsUpdateHelper(); |
| |
| void OnShutdown(); |
| void OnOriginAccessUpdated(); |
| |
| base::OnceClosure dispatch_event_; |
| base::CallbackListSubscription shutdown_subscription_; |
| base::WeakPtrFactory<NetworkPermissionsUpdateHelper> weak_factory_{this}; |
| }; |
| |
| // static |
| void PermissionsUpdater::NetworkPermissionsUpdateHelper::UpdatePermissions( |
| content::BrowserContext* browser_context, |
| EventType event_type, |
| scoped_refptr<const Extension> extension, |
| const PermissionSet& changed, |
| base::OnceClosure completion_callback) { |
| // If there is no difference in allowlist/blocklist for the extension, we can |
| // synchronously finish it without updating the CORS access list. |
| // We do not apply this optimization for POLICY event_type, since callers do |
| // not pass effective |changed| argument. |
| if (event_type != POLICY && changed.effective_hosts().is_empty()) { |
| PermissionsUpdater::NotifyPermissionsUpdated( |
| browser_context, event_type, std::move(extension), changed.Clone(), |
| std::move(completion_callback)); |
| return; |
| } |
| |
| NetworkPermissionsUpdateHelper* helper = new NetworkPermissionsUpdateHelper( |
| browser_context, |
| base::BindOnce(&PermissionsUpdater::NotifyPermissionsUpdated, |
| browser_context, event_type, extension, changed.Clone(), |
| std::move(completion_callback))); |
| |
| // After an asynchronous call below, the helper will call |
| // NotifyPermissionsUpdated if the profile is still valid. |
| NetworkPermissionsUpdater::UpdateExtension( |
| *browser_context, *extension, |
| NetworkPermissionsUpdater::ContextSet::kAllRelatedContexts, |
| base::BindOnce(&NetworkPermissionsUpdateHelper::OnOriginAccessUpdated, |
| helper->weak_factory_.GetWeakPtr())); |
| } |
| |
| // static |
| void PermissionsUpdater::NetworkPermissionsUpdateHelper:: |
| UpdateDefaultPolicyHostRestrictions( |
| content::BrowserContext* browser_context, |
| const URLPatternSet& default_runtime_blocked_hosts, |
| const URLPatternSet& default_runtime_allowed_hosts) { |
| NetworkPermissionsUpdateHelper* helper = new NetworkPermissionsUpdateHelper( |
| browser_context, |
| base::BindOnce( |
| &PermissionsUpdater::NotifyDefaultPolicyHostRestrictionsUpdated, |
| browser_context, default_runtime_blocked_hosts.Clone(), |
| default_runtime_allowed_hosts.Clone())); |
| |
| NetworkPermissionsUpdater::UpdateAllExtensions( |
| *browser_context, |
| base::BindOnce(&NetworkPermissionsUpdateHelper::OnOriginAccessUpdated, |
| helper->weak_factory_.GetWeakPtr())); |
| } |
| |
| PermissionsUpdater::NetworkPermissionsUpdateHelper:: |
| NetworkPermissionsUpdateHelper(content::BrowserContext* browser_context, |
| base::OnceClosure dispatch_event) |
| : dispatch_event_(std::move(dispatch_event)), |
| shutdown_subscription_( |
| PermissionsUpdaterShutdownNotifierFactory::GetInstance() |
| ->Get(browser_context) |
| ->Subscribe(base::BindRepeating( |
| &NetworkPermissionsUpdateHelper::OnShutdown, |
| base::Unretained(this)))) {} |
| |
| PermissionsUpdater::NetworkPermissionsUpdateHelper:: |
| ~NetworkPermissionsUpdateHelper() {} |
| |
| void PermissionsUpdater::NetworkPermissionsUpdateHelper::OnShutdown() { |
| // The profile is shutting down. Don't dispatch the permissions updated |
| // event, and clean up the dangling references. |
| delete this; |
| } |
| |
| void PermissionsUpdater::NetworkPermissionsUpdateHelper:: |
| OnOriginAccessUpdated() { |
| // The origin access list was successfully updated; dispatch the event |
| // and clean up dangling references. |
| std::move(dispatch_event_).Run(); |
| delete this; |
| } |
| |
| PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context) |
| : PermissionsUpdater(browser_context, INIT_FLAG_NONE) {} |
| |
| PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context, |
| InitFlag init_flag) |
| : browser_context_(browser_context), init_flag_(init_flag) {} |
| |
| PermissionsUpdater::~PermissionsUpdater() {} |
| |
| void PermissionsUpdater::GrantOptionalPermissions( |
| const Extension& extension, |
| const PermissionSet& permissions, |
| base::OnceClosure completion_callback) { |
| CHECK(PermissionsParser::GetOptionalPermissions(&extension) |
| .Contains(permissions)) |
| << "Cannot add optional permissions that are not " |
| << "specified in the manifest."; |
| |
| // Granted optional permissions are stored in both the granted permissions (so |
| // we don't later disable the extension when we check the active permissions |
| // against the granted set to determine if there's a permissions increase) and |
| // the granted runtime permissions (so they don't get withheld with runtime |
| // host permissions enabled). They're also added to the active set, which is |
| // the permission set stored in preferences representing the extension's |
| // currently-desired permission state. |
| // TODO(tjudkins): The reasoning for this doesn't entirely hold true now that |
| // we check both the granted permissions and runtime permissions to detect a |
| // permission increase. We should address this as we continue working on |
| // reducing the different ways we store permissions into a unified concept. |
| constexpr int permissions_store_mask = |
| kActivePermissions | kGrantedPermissions | kRuntimeGrantedPermissions; |
| AddPermissionsImpl(extension, permissions, permissions_store_mask, |
| permissions, std::move(completion_callback)); |
| } |
| |
| void PermissionsUpdater::GrantRuntimePermissions( |
| const Extension& extension, |
| const PermissionSet& permissions, |
| base::OnceClosure completion_callback) { |
| // We don't want to grant the extension object/process more privilege than it |
| // requested, even if the user grants additional permission. For instance, if |
| // the extension requests https://maps.google.com and the user grants |
| // https://*.google.com, we only want to grant the extension itself |
| // https://maps.google.com. Since we updated the prefs with the exact |
| // granted permissions (*.google.com), if the extension later requests |
| // increased permissions that are already covered, they will be auto-granted. |
| |
| // Determine which permissions to add to the extension. |
| const PermissionSet& withheld = |
| extension.permissions_data()->withheld_permissions(); |
| |
| // We add the intersection of any permissions that were withheld and the |
| // permissions that were granted. Since these might not be directly |
| // overlapping, we need to use a detailed intersection behavior here. |
| std::unique_ptr<const PermissionSet> active_permissions_to_add = |
| PermissionSet::CreateIntersection( |
| withheld, permissions, |
| URLPatternSet::IntersectionBehavior::kDetailed); |
| CHECK(extension.permissions_data()->withheld_permissions().Contains( |
| *active_permissions_to_add)) |
| << "Cannot add runtime granted permissions that were not withheld."; |
| |
| // Adding runtime granted permissions does not add permissions to the |
| // granted or active permissions store, so that behavior taken with the |
| // runtime host permissions feature is confined to when the experiment is |
| // enabled. |
| constexpr int permissions_store_mask = kRuntimeGrantedPermissions; |
| AddPermissionsImpl(extension, *active_permissions_to_add, |
| permissions_store_mask, permissions, |
| std::move(completion_callback)); |
| } |
| |
| void PermissionsUpdater::RevokeOptionalPermissions( |
| const Extension& extension, |
| const PermissionSet& permissions, |
| RemoveType remove_type, |
| base::OnceClosure completion_callback) { |
| CHECK(PermissionsParser::GetOptionalPermissions(&extension) |
| .Contains(permissions)) |
| << "Cannot remove optional permissions that are not " |
| << "specified in the manifest."; |
| |
| // Revoked optional permissions are removed from granted and runtime-granted |
| // permissions only if the user, and not the extension, removed them (i.e., |
| // `remove_type` == REMOVE_HARD). This allows the extension to add them again |
| // without prompting the user. They are always removed from the active set, |
| // which is the set of permissions the extension currently requests. |
| int permissions_store_mask = kActivePermissions; |
| if (remove_type == REMOVE_HARD) { |
| permissions_store_mask |= kGrantedPermissions | kRuntimeGrantedPermissions; |
| |
| // We don't allow the hard-removal of user-permitted sites on a per- |
| // extension basis. Instead, these permissions must be removed by removing |
| // the user-permitted site entry. If this changes, we'll need to adjust |
| // this to add back these sites, as we do in RevokeRuntimePermissions(). |
| #if DCHECK_IS_ON() |
| URLPatternSet user_permitted_sites = |
| GetUserPermittedPatternSet(*browser_context_); |
| PermissionSet user_permitted_set( |
| APIPermissionSet(), ManifestPermissionSet(), |
| user_permitted_sites.Clone(), user_permitted_sites.Clone()); |
| std::unique_ptr<const PermissionSet> user_permitted_being_removed = |
| PermissionSet::CreateIntersection( |
| permissions, user_permitted_set, |
| URLPatternSet::IntersectionBehavior::kDetailed); |
| DCHECK(user_permitted_being_removed->effective_hosts().is_empty()) |
| << "Attempting to hard-remove optional permission to user-permitted " |
| "sites: " |
| << user_permitted_being_removed->effective_hosts(); |
| #endif |
| } |
| |
| // Revoking optional permissions is usually done by the extension, so we allow |
| // revoking user-permitted sites (the extension can opt-out of having |
| // permissions). So in this case, the new active permissions are simply the |
| // current active minus any revoked permissions. |
| std::unique_ptr<const PermissionSet> new_active_permissions = |
| PermissionSet::CreateDifference( |
| extension.permissions_data()->active_permissions(), permissions); |
| |
| RemovePermissionsImpl(extension, std::move(new_active_permissions), |
| permissions, permissions_store_mask, |
| std::move(completion_callback)); |
| } |
| |
| void PermissionsUpdater::RevokeRuntimePermissions( |
| const Extension& extension, |
| const PermissionSet& permissions, |
| base::OnceClosure completion_callback) { |
| // Similar to the process in adding permissions, we might be revoking more |
| // permissions than the extension currently has explicit access to. For |
| // instance, we might be revoking https://*.google.com/* even if the extension |
| // only has https://maps.google.com/*. |
| const PermissionSet& active = |
| extension.permissions_data()->active_permissions(); |
| |
| // Unlike adding permissions, we should know that any permissions we remove |
| // are a subset of the permissions the extension has active (because we only |
| // allow removal origins and the extension can't have a broader origin than |
| // what it has granted). Because of this, we can just look for any patterns |
| // contained in both sets. |
| std::unique_ptr<const PermissionSet> active_permissions_to_remove = |
| PermissionSet::CreateIntersection( |
| active, permissions, |
| URLPatternSet::IntersectionBehavior::kPatternsContainedByBoth); |
| |
| CHECK(active.Contains(*active_permissions_to_remove)) |
| << "Cannot remove permissions that are not active."; |
| CHECK(GetRevokablePermissions(&extension)->Contains(permissions)) |
| << "Cannot remove non-revokable permissions."; |
| |
| // Calculate a set of permissions to keep active on the extension, even if |
| // they were included in the removal set. This includes chrome://favicon |
| // (which would be included in `active_permissions_to_remove` if the removal |
| // set is <all_urls>) and any sites the user indicated all extensions may |
| // always run on. |
| std::unique_ptr<const PermissionSet> permissions_to_keep; |
| { |
| URLPatternSet explicit_hosts; |
| URLPatternSet scriptable_hosts; |
| |
| // Don't allow removing chrome://favicon, if it was previously granted. |
| for (const auto& pattern : active_permissions_to_remove->explicit_hosts()) { |
| bool is_chrome_favicon = pattern.scheme() == content::kChromeUIScheme && |
| pattern.host() == chrome::kChromeUIFaviconHost; |
| if (is_chrome_favicon) { |
| explicit_hosts.AddPattern(pattern); |
| break; |
| } |
| } |
| |
| // If the corresponding feature is enabled, add in user-permitted sites. |
| if (base::FeatureList::IsEnabled( |
| extensions_features::kExtensionsMenuAccessControl)) { |
| URLPatternSet always_permitted_set = |
| GetUserPermittedPatternSet(*browser_context_); |
| explicit_hosts.AddPatterns(always_permitted_set); |
| scriptable_hosts.AddPatterns(always_permitted_set); |
| } |
| |
| PermissionSet permitted_set(APIPermissionSet(), ManifestPermissionSet(), |
| std::move(explicit_hosts), |
| std::move(scriptable_hosts)); |
| |
| permissions_to_keep = PermissionSet::CreateIntersection( |
| *active_permissions_to_remove, permitted_set, |
| URLPatternSet::IntersectionBehavior::kDetailed); |
| } |
| |
| // Calculate the new set of active permissions. This is the current |
| // permissions minus the permissions to remove, but then adding back in any |
| // of the permissions we've explicitly identified as those we should keep. |
| std::unique_ptr<const PermissionSet> new_active_permissions = |
| PermissionSet::CreateDifference(active, *active_permissions_to_remove); |
| new_active_permissions = |
| PermissionSet::CreateUnion(*new_active_permissions, *permissions_to_keep); |
| |
| // Runtime permissions have a separate store in prefs. |
| // Note that we remove all the permissions in `permissions` from |
| // runtime-granted permissions. User-permitted sites are granted |
| // separately, and not considered runtime-granted permissions. This ensures |
| // that when a user changes a site from permitted to non-permitted or vice |
| // versa, and extension's specific stored permissions are unaffected. |
| constexpr int permissions_store_mask = kRuntimeGrantedPermissions; |
| RemovePermissionsImpl(extension, std::move(new_active_permissions), |
| permissions, permissions_store_mask, |
| std::move(completion_callback)); |
| } |
| |
| void PermissionsUpdater::ApplyPolicyHostRestrictions( |
| const Extension& extension) { |
| ExtensionManagement* management = |
| ExtensionManagementFactory::GetForBrowserContext(browser_context_); |
| if (management->UsesDefaultPolicyHostRestrictions(&extension)) { |
| SetUsesDefaultHostRestrictions(&extension); |
| } else { |
| SetPolicyHostRestrictions(&extension, |
| management->GetPolicyBlockedHosts(&extension), |
| management->GetPolicyAllowedHosts(&extension)); |
| } |
| } |
| |
| void PermissionsUpdater::SetPolicyHostRestrictions( |
| const Extension* extension, |
| const URLPatternSet& runtime_blocked_hosts, |
| const URLPatternSet& runtime_allowed_hosts) { |
| extension->permissions_data()->SetPolicyHostRestrictions( |
| runtime_blocked_hosts, runtime_allowed_hosts); |
| |
| // Update the BrowserContext origin lists, and send notification to the |
| // currently running renderers of the runtime block hosts settings. |
| NetworkPermissionsUpdateHelper::UpdatePermissions( |
| browser_context_, POLICY, extension, PermissionSet(), base::DoNothing()); |
| } |
| |
| void PermissionsUpdater::SetUsesDefaultHostRestrictions( |
| const Extension* extension) { |
| extension->permissions_data()->SetUsesDefaultHostRestrictions(); |
| NetworkPermissionsUpdateHelper::UpdatePermissions( |
| browser_context_, POLICY, extension, PermissionSet(), base::DoNothing()); |
| } |
| |
| void PermissionsUpdater::SetDefaultPolicyHostRestrictions( |
| const URLPatternSet& default_runtime_blocked_hosts, |
| const URLPatternSet& default_runtime_allowed_hosts) { |
| DCHECK_EQ(0, init_flag_ & INIT_FLAG_TRANSIENT); |
| |
| PermissionsData::SetDefaultPolicyHostRestrictions( |
| util::GetBrowserContextId(browser_context_), |
| default_runtime_blocked_hosts, default_runtime_allowed_hosts); |
| |
| // Update the BrowserContext origin lists, and send notification to the |
| // currently running renderers of the runtime block hosts settings. |
| NetworkPermissionsUpdateHelper::UpdateDefaultPolicyHostRestrictions( |
| browser_context_, default_runtime_blocked_hosts, |
| default_runtime_allowed_hosts); |
| } |
| |
| void PermissionsUpdater::RemovePermissionsUnsafe( |
| const Extension* extension, |
| const PermissionSet& to_remove) { |
| const PermissionSet& active = |
| extension->permissions_data()->active_permissions(); |
| std::unique_ptr<const PermissionSet> total = |
| PermissionSet::CreateDifference(active, to_remove); |
| // |successfully_removed| might not equal |to_remove| if |to_remove| contains |
| // permissions the extension didn't have. |
| std::unique_ptr<const PermissionSet> successfully_removed = |
| PermissionSet::CreateDifference(active, *total); |
| |
| // TODO(devlin): This seems wrong. Since these permissions are being removed |
| // by enterprise policy, we should not update the active permissions set in |
| // preferences. That way, if the enterprise policy is changed, the removed |
| // permissions would be re-added. |
| ExtensionPrefs::Get(browser_context_) |
| ->SetDesiredActivePermissions(extension->id(), *total); |
| |
| SetPermissions(extension, std::move(total)); |
| NetworkPermissionsUpdateHelper::UpdatePermissions( |
| browser_context_, REMOVED, extension, *successfully_removed, |
| base::DoNothing()); |
| } |
| |
| std::unique_ptr<const PermissionSet> |
| PermissionsUpdater::GetRevokablePermissions(const Extension* extension) const { |
| // Any permissions not required by the extension are revokable. |
| const PermissionSet& required = |
| PermissionsParser::GetRequiredPermissions(extension); |
| std::unique_ptr<const PermissionSet> revokable_permissions = |
| PermissionSet::CreateDifference( |
| extension->permissions_data()->active_permissions(), required); |
| |
| // Additionally, some required permissions may be revokable if they can be |
| // withheld by the ScriptingPermissionsModifier. |
| std::unique_ptr<const PermissionSet> revokable_scripting_permissions = |
| PermissionsManager::Get(browser_context_) |
| ->GetRevokablePermissions(*extension); |
| |
| if (revokable_scripting_permissions) { |
| revokable_permissions = PermissionSet::CreateUnion( |
| *revokable_permissions, *revokable_scripting_permissions); |
| } |
| return revokable_permissions; |
| } |
| |
| void PermissionsUpdater::GrantActivePermissions(const Extension* extension) { |
| CHECK(extension); |
| |
| ExtensionPrefs::Get(browser_context_) |
| ->AddGrantedPermissions( |
| extension->id(), extension->permissions_data()->active_permissions()); |
| } |
| |
| void PermissionsUpdater::InitializePermissions(const Extension* extension) { |
| std::unique_ptr<const PermissionSet> desired_permissions_wrapper; |
| const PermissionSet* desired_permissions = nullptr; |
| |
| PermissionsManager* permissions_manager = |
| PermissionsManager::Get(browser_context_); |
| DCHECK(permissions_manager); |
| |
| // If |extension| is a transient dummy extension, we do not want to look for |
| // it in preferences. |
| if (init_flag_ & INIT_FLAG_TRANSIENT) { |
| desired_permissions = &extension->permissions_data()->active_permissions(); |
| } else { |
| desired_permissions_wrapper = |
| permissions_manager->GetBoundedExtensionDesiredPermissions(*extension); |
| desired_permissions = desired_permissions_wrapper.get(); |
| } |
| |
| std::unique_ptr<const PermissionSet> granted_permissions = |
| permissions_manager->GetEffectivePermissionsToGrant(*extension, |
| *desired_permissions); |
| |
| if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) { |
| // Set the desired permissions in prefs. |
| // - For new installs, this initializes the desired active permissions. |
| // - For updates, this ensures the desired active permissions contain any |
| // newly-added permissions and removes any no-longer-requested |
| // permissions. |
| // - For pref corruption, this resets the prefs to a sane state. |
| // - This also resets prefs from https://crbug.com/1343643, in which |
| // desired active permissions may not have included all required |
| // permissions. |
| ExtensionPrefs::Get(browser_context_) |
| ->SetDesiredActivePermissions(extension->id(), *desired_permissions); |
| |
| extension->permissions_data()->SetContextId( |
| util::GetBrowserContextId(browser_context_)); |
| |
| // Apply per-extension policy if set. |
| ApplyPolicyHostRestrictions(*extension); |
| } |
| |
| SetPermissions(extension, std::move(granted_permissions)); |
| } |
| |
| void PermissionsUpdater::AddPermissionsForTesting( |
| const Extension& extension, |
| const PermissionSet& permissions) { |
| AddPermissionsImpl(extension, permissions, kNone, permissions, |
| base::DoNothing()); |
| } |
| |
| void PermissionsUpdater::SetPermissions( |
| const Extension* extension, |
| std::unique_ptr<const PermissionSet> new_active) { |
| // Calculate the withheld permissions as any permissions that were required, |
| // but are not in the active set. |
| const PermissionSet& required = |
| PermissionsParser::GetRequiredPermissions(extension); |
| // TODO(https://crbug.com/869403): Currently, withheld permissions should only |
| // contain permissions withheld by the runtime host permissions feature. |
| // However, there could possibly be API permissions that were removed from the |
| // active set by enterprise policy. These shouldn't go in the withheld |
| // permission set, since withheld permissions are generally supposed to be |
| // grantable. Currently, we can deal with this because all permissions |
| // withheld by runtime host permissions are explicit or scriptable hosts, and |
| // all permissions blocked by enterprise are API permissions. So to get the |
| // set of runtime-hosts-withheld permissions, we just look at the delta in the |
| // URLPatternSets. However, this is very fragile, and should be dealt with |
| // more robustly. |
| std::unique_ptr<const PermissionSet> new_withheld = |
| PermissionSet::CreateDifference( |
| PermissionSet(APIPermissionSet(), ManifestPermissionSet(), |
| required.explicit_hosts().Clone(), |
| required.scriptable_hosts().Clone()), |
| *new_active); |
| |
| extension->permissions_data()->SetPermissions(std::move(new_active), |
| std::move(new_withheld)); |
| } |
| |
| // static |
| void PermissionsUpdater::NotifyPermissionsUpdated( |
| content::BrowserContext* browser_context, |
| EventType event_type, |
| scoped_refptr<const Extension> extension, |
| std::unique_ptr<const PermissionSet> changed, |
| base::OnceClosure completion_callback) { |
| if ((changed->IsEmpty() && event_type != POLICY) || |
| browser_context->ShutdownStarted()) { |
| std::move(completion_callback).Run(); |
| return; |
| } |
| |
| PermissionsManager::UpdateReason reason; |
| events::HistogramValue histogram_value = events::UNKNOWN; |
| const char* event_name = nullptr; |
| Profile* profile = Profile::FromBrowserContext(browser_context); |
| |
| if (event_type == REMOVED) { |
| reason = PermissionsManager::UpdateReason::kRemoved; |
| histogram_value = events::PERMISSIONS_ON_REMOVED; |
| event_name = permissions::OnRemoved::kEventName; |
| } else if (event_type == ADDED) { |
| reason = PermissionsManager::UpdateReason::kAdded; |
| histogram_value = events::PERMISSIONS_ON_ADDED; |
| event_name = permissions::OnAdded::kEventName; |
| } else { |
| DCHECK_EQ(POLICY, event_type); |
| reason = PermissionsManager::UpdateReason::kPolicy; |
| } |
| |
| // Notify other APIs or interested parties. |
| PermissionsManager::Get(browser_context) |
| ->NotifyExtensionPermissionsUpdated(*extension, *changed, reason); |
| |
| // Send the new permissions to the renderers. |
| for (RenderProcessHost::iterator host_iterator( |
| RenderProcessHost::AllHostsIterator()); |
| !host_iterator.IsAtEnd(); host_iterator.Advance()) { |
| RenderProcessHost* host = host_iterator.GetCurrentValue(); |
| if (host->IsInitializedAndNotDead() && |
| profile->IsSameOrParent( |
| Profile::FromBrowserContext(host->GetBrowserContext()))) { |
| mojom::Renderer* renderer = |
| RendererStartupHelperFactory::GetForBrowserContext( |
| host->GetBrowserContext()) |
| ->GetRenderer(host); |
| if (renderer) { |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| renderer->UpdatePermissions( |
| extension->id(), |
| std::move(*permissions_data->active_permissions().Clone()), |
| std::move(*permissions_data->withheld_permissions().Clone()), |
| permissions_data->policy_blocked_hosts(), |
| permissions_data->policy_allowed_hosts(), |
| permissions_data->UsesDefaultPolicyHostRestrictions()); |
| } |
| } |
| } |
| |
| // Trigger the onAdded and onRemoved events in the extension. We explicitly |
| // don't do this for policy-related events. |
| EventRouter* event_router = |
| event_name ? EventRouter::Get(browser_context) : nullptr; |
| if (event_router) { |
| base::Value::List event_args; |
| std::unique_ptr<api::permissions::Permissions> permissions = |
| PackPermissionSet(*changed); |
| event_args.Append(permissions->ToValue()); |
| auto event = std::make_unique<Event>( |
| histogram_value, event_name, std::move(event_args), browser_context); |
| event_router->DispatchEventToExtension(extension->id(), std::move(event)); |
| } |
| |
| std::move(completion_callback).Run(); |
| } |
| |
| // static |
| void PermissionsUpdater::NotifyDefaultPolicyHostRestrictionsUpdated( |
| content::BrowserContext* browser_context, |
| const URLPatternSet default_runtime_blocked_hosts, |
| const URLPatternSet default_runtime_allowed_hosts) { |
| Profile* profile = Profile::FromBrowserContext(browser_context); |
| |
| // Send the new policy to the renderers. |
| for (RenderProcessHost::iterator host_iterator( |
| RenderProcessHost::AllHostsIterator()); |
| !host_iterator.IsAtEnd(); host_iterator.Advance()) { |
| RenderProcessHost* host = host_iterator.GetCurrentValue(); |
| if (host->IsInitializedAndNotDead() && |
| profile->IsSameOrParent( |
| Profile::FromBrowserContext(host->GetBrowserContext()))) { |
| mojom::Renderer* renderer = |
| RendererStartupHelperFactory::GetForBrowserContext( |
| host->GetBrowserContext()) |
| ->GetRenderer(host); |
| if (renderer) { |
| renderer->UpdateDefaultPolicyHostRestrictions( |
| default_runtime_blocked_hosts.Clone(), |
| default_runtime_allowed_hosts.Clone()); |
| } |
| } |
| } |
| } |
| |
| void PermissionsUpdater::AddPermissionsImpl( |
| const Extension& extension, |
| const PermissionSet& active_permissions_to_add, |
| int prefs_permissions_store_mask, |
| const PermissionSet& permissions_to_add_to_prefs, |
| base::OnceClosure completion_callback) { |
| std::unique_ptr<const PermissionSet> new_active = PermissionSet::CreateUnion( |
| active_permissions_to_add, |
| extension.permissions_data()->active_permissions()); |
| |
| SetPermissions(&extension, std::move(new_active)); |
| |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_); |
| if ((prefs_permissions_store_mask & kActivePermissions) != 0) { |
| prefs->AddDesiredActivePermissions(extension.id(), |
| permissions_to_add_to_prefs); |
| } |
| |
| if ((prefs_permissions_store_mask & kGrantedPermissions) != 0) { |
| prefs->AddGrantedPermissions(extension.id(), permissions_to_add_to_prefs); |
| } |
| |
| if ((prefs_permissions_store_mask & kRuntimeGrantedPermissions) != 0) { |
| prefs->AddRuntimeGrantedPermissions(extension.id(), |
| permissions_to_add_to_prefs); |
| } |
| |
| NetworkPermissionsUpdateHelper::UpdatePermissions( |
| browser_context_, ADDED, &extension, active_permissions_to_add, |
| std::move(completion_callback)); |
| } |
| |
| void PermissionsUpdater::RemovePermissionsImpl( |
| const Extension& extension, |
| std::unique_ptr<const PermissionSet> new_active_permissions, |
| const PermissionSet& permissions_to_remove_from_prefs, |
| int prefs_permissions_store_mask, |
| base::OnceClosure completion_callback) { |
| SetPermissions(&extension, std::move(new_active_permissions)); |
| |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_); |
| if ((prefs_permissions_store_mask & kActivePermissions) != 0) { |
| prefs->RemoveDesiredActivePermissions(extension.id(), |
| permissions_to_remove_from_prefs); |
| } |
| |
| // NOTE: Currently, this code path is only reached in unit tests. See comment |
| // above REMOVE_HARD in the header file. |
| if ((prefs_permissions_store_mask & kGrantedPermissions) != 0) { |
| prefs->RemoveGrantedPermissions(extension.id(), |
| permissions_to_remove_from_prefs); |
| } |
| |
| if ((prefs_permissions_store_mask & kRuntimeGrantedPermissions) != 0) { |
| prefs->RemoveRuntimeGrantedPermissions(extension.id(), |
| permissions_to_remove_from_prefs); |
| } |
| |
| // For the notification, we consider the changed set to be the set of |
| // permissions to remove from preferences, rather than the new active |
| // permissions (which can include things like user-permitted sites). |
| NetworkPermissionsUpdateHelper::UpdatePermissions( |
| browser_context_, REMOVED, &extension, permissions_to_remove_from_prefs, |
| std::move(completion_callback)); |
| } |
| |
| // static |
| void PermissionsUpdater::EnsureAssociatedFactoryBuilt() { |
| PermissionsUpdaterShutdownNotifierFactory::GetInstance(); |
| } |
| |
| } // namespace extensions |