blob: 5ff4111d5bbb5538787bf8c2374542b0587737f8 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/permissions_manager.h"
#include <memory>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/ranges/algorithm.h"
#include "base/values.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/network_permissions_updater.h"
#include "extensions/browser/pref_names.h"
#include "extensions/browser/pref_types.h"
#include "extensions/browser/renderer_startup_helper.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/mojom/renderer.mojom.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
namespace extensions {
namespace {
// Entries of `kUserPermissions` dictionary.
const char kRestrictedSites[] = "restricted_sites";
const char kPermittedSites[] = "permitted_sites";
// Sets `pref` in `extension_prefs` if it doesn't exist, and appends
// `origin` to its list.
void AddSiteToPrefs(ExtensionPrefs* extension_prefs,
const char* pref,
const url::Origin& origin) {
std::unique_ptr<prefs::ScopedDictionaryPrefUpdate> update =
extension_prefs->CreatePrefUpdate(kUserPermissions);
base::Value::List* list = nullptr;
bool pref_exists = (*update)->GetListWithoutPathExpansion(pref, &list);
if (pref_exists) {
list->Append(origin.Serialize());
} else {
base::Value::List sites;
sites.Append(origin.Serialize());
(*update)->SetKey(pref, base::Value(std::move(sites)));
}
}
// Removes `origin` from `pref` in `extension_prefs`.
void RemoveSiteFromPrefs(ExtensionPrefs* extension_prefs,
const char* pref,
const url::Origin& origin) {
std::unique_ptr<prefs::ScopedDictionaryPrefUpdate> update =
extension_prefs->CreatePrefUpdate(kUserPermissions);
base::Value::List* list = nullptr;
(*update)->GetListWithoutPathExpansion(pref, &list);
DCHECK(list);
list->EraseValue(base::Value(origin.Serialize()));
}
// Returns sites from `pref` in `extension_prefs`.
std::set<url::Origin> GetSitesFromPrefs(ExtensionPrefs* extension_prefs,
const char* pref) {
const base::Value::Dict& user_permissions =
extension_prefs->GetPrefAsDictionary(kUserPermissions);
std::set<url::Origin> sites;
auto* list = user_permissions.FindList(pref);
if (!list)
return sites;
for (const auto& site : *list) {
const std::string* site_as_string = site.GetIfString();
if (!site_as_string)
continue;
GURL site_as_url(*site_as_string);
if (!site_as_url.is_valid())
continue;
url::Origin origin = url::Origin::Create(site_as_url);
sites.insert(origin);
}
return sites;
}
// Returns the set of permissions that the extension is allowed to have after
// withholding any that should not be granted. `desired_permissions` is the set
// of permissions the extension wants, `runtime_granted_permissions` are the
// permissions the user explicitly granted the extension at runtime, and
// `user_granted_permissions` are permissions that the user has indicated any
// extension may have.
// This should only be called for extensions that have permissions withheld.
std::unique_ptr<PermissionSet> GetAllowedPermissionsAfterWithholding(
const PermissionSet& desired_permissions,
const PermissionSet& runtime_granted_permissions,
const PermissionSet& user_granted_permissions) {
// 1) Take the set of all allowed permissions. This is the union of
// runtime-granted permissions (where the user said "this extension may run
// on this site") and `user_granted_permissions` (sites the user allows any
// extension to run on).
std::unique_ptr<PermissionSet> allowed_permissions =
PermissionSet::CreateUnion(user_granted_permissions,
runtime_granted_permissions);
// 2) Add in any always-approved hosts that shouldn't be removed (such as
// chrome://favicon).
ExtensionsBrowserClient::Get()->AddAdditionalAllowedHosts(
desired_permissions, allowed_permissions.get());
// 3) Finalize the allowed set. Since we don't allow withholding of API and
// manifest permissions, the allowed set always contains all (bounded)
// requested API and manifest permissions.
allowed_permissions->SetAPIPermissions(desired_permissions.apis().Clone());
allowed_permissions->SetManifestPermissions(
desired_permissions.manifest_permissions().Clone());
// 4) Calculate the set of permissions to give to the extension. This is the
// intersection of all permissions the extension is allowed to have
// (`allowed_permissions`) with all permissions the extension elected to
// have (`desired_permissions`).
// Said differently, we grant a permission if both the extension and the
// user approved it.
return PermissionSet::CreateIntersection(
*allowed_permissions, desired_permissions,
URLPatternSet::IntersectionBehavior::kDetailed);
}
// Returns whether the extension requests host permissions.
bool ExtensionRequestsHostPermissions(const Extension& extension) {
return !PermissionsParser::GetRequiredPermissions(&extension).IsEmpty() ||
!PermissionsParser::GetOptionalPermissions(&extension).IsEmpty();
}
class PermissionsManagerFactory : public BrowserContextKeyedServiceFactory {
public:
PermissionsManagerFactory();
~PermissionsManagerFactory() override = default;
PermissionsManagerFactory(const PermissionsManagerFactory&) = delete;
const PermissionsManagerFactory& operator=(const PermissionsManagerFactory&) =
delete;
PermissionsManager* GetForBrowserContext(
content::BrowserContext* browser_context);
private:
// BrowserContextKeyedServiceFactory
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* browser_context) const override;
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* browser_context) const override;
};
PermissionsManagerFactory::PermissionsManagerFactory()
: BrowserContextKeyedServiceFactory(
"PermissionsManager",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(ExtensionRegistryFactory::GetInstance());
}
PermissionsManager* PermissionsManagerFactory::GetForBrowserContext(
content::BrowserContext* browser_context) {
return static_cast<PermissionsManager*>(
GetServiceForBrowserContext(browser_context, /*create=*/true));
}
content::BrowserContext* PermissionsManagerFactory::GetBrowserContextToUse(
content::BrowserContext* browser_context) const {
return ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context);
}
KeyedService* PermissionsManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* browser_context) const {
return new PermissionsManager(browser_context);
}
} // namespace
// Implementation of UserPermissionsSettings.
PermissionsManager::UserPermissionsSettings::UserPermissionsSettings() =
default;
PermissionsManager::UserPermissionsSettings::~UserPermissionsSettings() =
default;
// Implementation of PermissionsManager.
PermissionsManager::PermissionsManager(content::BrowserContext* browser_context)
: browser_context_(browser_context),
extension_prefs_(ExtensionPrefs::Get(browser_context)) {
user_permissions_.restricted_sites =
GetSitesFromPrefs(extension_prefs_, kRestrictedSites);
if (base::FeatureList::IsEnabled(
extensions_features::
kExtensionsMenuAccessControlWithPermittedSites)) {
user_permissions_.permitted_sites =
GetSitesFromPrefs(extension_prefs_, kPermittedSites);
}
}
PermissionsManager::~PermissionsManager() {
user_permissions_.restricted_sites.clear();
user_permissions_.permitted_sites.clear();
}
// static
PermissionsManager* PermissionsManager::Get(
content::BrowserContext* browser_context) {
return static_cast<PermissionsManagerFactory*>(GetFactory())
->GetForBrowserContext(browser_context);
}
// static
BrowserContextKeyedServiceFactory* PermissionsManager::GetFactory() {
static base::NoDestructor<PermissionsManagerFactory> g_factory;
return g_factory.get();
}
// static
void PermissionsManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(kUserPermissions.name);
}
void PermissionsManager::UpdateUserSiteSetting(const url::Origin& origin,
UserSiteSetting site_setting) {
switch (site_setting) {
case UserSiteSetting::kGrantAllExtensions:
// Granting access to all extensions is allowed iff feature is
// enabled.
DCHECK(base::FeatureList::IsEnabled(
extensions_features::kExtensionsMenuAccessControlWithPermittedSites));
AddUserPermittedSite(origin);
break;
case UserSiteSetting::kBlockAllExtensions:
AddUserRestrictedSite(origin);
break;
case UserSiteSetting::kCustomizeByExtension:
if (base::FeatureList::IsEnabled(
extensions_features::
kExtensionsMenuAccessControlWithPermittedSites)) {
RemoveUserPermittedSite(origin);
}
RemoveUserRestrictedSite(origin);
break;
}
}
void PermissionsManager::AddUserRestrictedSite(const url::Origin& origin) {
if (base::Contains(user_permissions_.restricted_sites, origin))
return;
// Origin cannot be both restricted and permitted.
RemovePermittedSiteAndUpdatePrefs(origin);
user_permissions_.restricted_sites.insert(origin);
AddSiteToPrefs(extension_prefs_, kRestrictedSites, origin);
OnUserPermissionsSettingsChanged();
}
void PermissionsManager::RemoveUserRestrictedSite(const url::Origin& origin) {
if (RemoveRestrictedSiteAndUpdatePrefs(origin))
OnUserPermissionsSettingsChanged();
}
void PermissionsManager::AddUserPermittedSite(const url::Origin& origin) {
DCHECK(base::FeatureList::IsEnabled(
extensions_features::kExtensionsMenuAccessControlWithPermittedSites));
if (base::Contains(user_permissions_.permitted_sites, origin)) {
return;
}
// Origin cannot be both restricted and permitted.
RemoveRestrictedSiteAndUpdatePrefs(origin);
user_permissions_.permitted_sites.insert(origin);
AddSiteToPrefs(extension_prefs_, kPermittedSites, origin);
OnUserPermissionsSettingsChanged();
}
void PermissionsManager::UpdatePermissionsWithUserSettings(
const Extension& extension,
const PermissionSet& user_permitted_set) {
// If either user cannot be affected by hbe affected by host permissions
// policy-installed extensions) or the user has not withheld any permissions
// for the extension, then we don't need to do anything - the extension
// already has all its requested permissions.
if (!CanAffectExtension(extension) ||
!HasWithheldHostPermissions(extension)) {
return;
}
std::unique_ptr<PermissionSet> new_active_permissions =
GetAllowedPermissionsAfterWithholding(
*GetBoundedExtensionDesiredPermissions(extension),
*GetRuntimePermissionsFromPrefs(extension), user_permitted_set);
// Calculate the new withheld permissions; these are any required permissions
// that are not in the new active set.
std::unique_ptr<PermissionSet> new_withheld_permissions =
PermissionSet::CreateDifference(
PermissionsParser::GetRequiredPermissions(&extension),
*new_active_permissions);
// Set the new permissions on the extension.
extension.permissions_data()->SetPermissions(
std::move(new_active_permissions), std::move(new_withheld_permissions));
}
void PermissionsManager::RemoveUserPermittedSite(const url::Origin& origin) {
DCHECK(base::FeatureList::IsEnabled(
extensions_features::kExtensionsMenuAccessControlWithPermittedSites));
if (RemovePermittedSiteAndUpdatePrefs(origin))
OnUserPermissionsSettingsChanged();
}
const PermissionsManager::UserPermissionsSettings&
PermissionsManager::GetUserPermissionsSettings() const {
return user_permissions_;
}
PermissionsManager::UserSiteSetting PermissionsManager::GetUserSiteSetting(
const url::Origin& origin) const {
if (user_permissions_.permitted_sites.find(origin) !=
user_permissions_.permitted_sites.end()) {
return UserSiteSetting::kGrantAllExtensions;
}
if (user_permissions_.restricted_sites.find(origin) !=
user_permissions_.restricted_sites.end()) {
return UserSiteSetting::kBlockAllExtensions;
}
return UserSiteSetting::kCustomizeByExtension;
}
PermissionsManager::UserSiteAccess PermissionsManager::GetUserSiteAccess(
const Extension& extension,
const GURL& gurl) const {
DCHECK(
!extension.permissions_data()->IsRestrictedUrl(gurl, /*error=*/nullptr));
// Extension with no host permissions but with active tab permission has "on
// click" access.
if (!ExtensionRequestsHostPermissions(extension) &&
HasActiveTabAndCanAccess(extension, gurl)) {
return UserSiteAccess::kOnClick;
}
ExtensionSiteAccess site_access = GetSiteAccess(extension, gurl);
if (site_access.has_all_sites_access) {
return UserSiteAccess::kOnAllSites;
}
if (site_access.has_site_access) {
return UserSiteAccess::kOnSite;
}
return UserSiteAccess::kOnClick;
}
PermissionsManager::ExtensionSiteAccess PermissionsManager::GetSiteAccess(
const Extension& extension,
const GURL& url) const {
PermissionsManager::ExtensionSiteAccess extension_access;
// Extension that doesn't request host permission has no access.
if (!ExtensionRequestsHostPermissions(extension)) {
return extension_access;
}
// Awkward holder object because permission sets are immutable, and when
// return from prefs, ownership is passed.
std::unique_ptr<const PermissionSet> permission_holder;
const PermissionSet* granted_permissions = nullptr;
if (!HasWithheldHostPermissions(extension)) {
// If the extension doesn't have any withheld permissions, we look at the
// current active permissions.
// TODO(devlin): This is clunky. It would be nice to have runtime-granted
// permissions be correctly populated in all cases, rather than looking at
// two different sets.
// TODO(devlin): This won't account for granted permissions that aren't
// currently active, even though the extension may re-request them (and be
// silently granted them) at any time.
granted_permissions = &extension.permissions_data()->active_permissions();
} else {
permission_holder = GetRuntimePermissionsFromPrefs(extension);
granted_permissions = permission_holder.get();
}
DCHECK(granted_permissions);
const bool is_restricted_site =
extension.permissions_data()->IsRestrictedUrl(url, /*error=*/nullptr);
// For indicating whether an extension has access to a site, we look at the
// granted permissions, which could include patterns that weren't explicitly
// requested. However, we should still indicate they are granted, so that the
// user can revoke them (and because if the extension does request them and
// they are already granted, they are silently added).
// The extension should never have access to restricted sites (even if a
// pattern matches, as it may for e.g. the webstore).
if (!is_restricted_site &&
granted_permissions->effective_hosts().MatchesSecurityOrigin(url)) {
extension_access.has_site_access = true;
}
const PermissionSet& withheld_permissions =
extension.permissions_data()->withheld_permissions();
// Be sure to check |access.has_site_access| in addition to withheld
// permissions, so that we don't indicate we've withheld permission if an
// extension is granted https://a.com/*, but has *://*/* withheld.
// We similarly don't show access as withheld for restricted sites, since
// withheld permissions should only include those that are conceivably
// grantable.
if (!is_restricted_site && !extension_access.has_site_access &&
withheld_permissions.effective_hosts().MatchesSecurityOrigin(url)) {
extension_access.withheld_site_access = true;
}
constexpr bool include_api_permissions = false;
if (granted_permissions->ShouldWarnAllHosts(include_api_permissions))
extension_access.has_all_sites_access = true;
if (withheld_permissions.ShouldWarnAllHosts(include_api_permissions) &&
!extension_access.has_all_sites_access) {
extension_access.withheld_all_sites_access = true;
}
return extension_access;
}
bool PermissionsManager::CanAffectExtension(const Extension& extension) const {
// Certain extensions are always exempt from having permissions withheld.
if (!util::CanWithholdPermissionsFromExtension(extension))
return false;
// The extension can be affected by runtime host permissions if it requests
// host permissions.
return ExtensionRequestsHostPermissions(extension);
}
bool PermissionsManager::CanUserSelectSiteAccess(
const Extension& extension,
const GURL& url,
UserSiteAccess site_access) const {
// Extensions cannot run on sites restricted to them (ever), so no type of
// site access is selectable.
if (extension.permissions_data()->IsRestrictedUrl(url, /*error=*/nullptr)) {
return false;
}
// The "on click" option is enabled if the extension has active tab,
// regardless of its granted host permissions.
if (site_access == PermissionsManager::UserSiteAccess::kOnClick &&
HasActiveTabAndCanAccess(extension, url)) {
return true;
}
if (!CanAffectExtension(extension)) {
return false;
}
PermissionsManager::ExtensionSiteAccess extension_access =
GetSiteAccess(extension, url);
switch (site_access) {
case UserSiteAccess::kOnClick:
// The "on click" option is only enabled if the extension has active tab,
// previously handled, or wants to always run on the site without user
// interaction.
return extension_access.has_site_access ||
extension_access.withheld_site_access;
case UserSiteAccess::kOnSite:
// The "on site" option is only enabled if the extension wants to
// always run on the site without user interaction.
return extension_access.has_site_access ||
extension_access.withheld_site_access;
case UserSiteAccess::kOnAllSites:
// The "on all sites" option is only enabled if the extension wants to be
// able to run everywhere.
return extension_access.has_all_sites_access ||
extension_access.withheld_all_sites_access;
}
}
bool PermissionsManager::HasGrantedHostPermission(const Extension& extension,
const GURL& url) const {
DCHECK(CanAffectExtension(extension));
return GetRuntimePermissionsFromPrefs(extension)
->effective_hosts()
.MatchesSecurityOrigin(url);
}
bool PermissionsManager::HasBroadGrantedHostPermissions(
const Extension& extension) {
// Don't consider API permissions in this case.
constexpr bool kIncludeApiPermissions = false;
return GetRuntimePermissionsFromPrefs(extension)->ShouldWarnAllHosts(
kIncludeApiPermissions);
}
bool PermissionsManager::HasWithheldHostPermissions(
const Extension& extension) const {
return extension_prefs_->GetWithholdingPermissions(extension.id());
}
bool PermissionsManager::HasActiveTabAndCanAccess(const Extension& extension,
const GURL& url) const {
return extension.permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kActiveTab) &&
!extension.permissions_data()->IsRestrictedUrl(url,
/*error=*/nullptr) &&
(!url.SchemeIsFile() ||
util::AllowFileAccess(extension.id(), browser_context_));
}
std::unique_ptr<PermissionSet>
PermissionsManager::GetRuntimePermissionsFromPrefs(
const Extension& extension) const {
std::unique_ptr<PermissionSet> permissions =
extension_prefs_->GetRuntimeGrantedPermissions(extension.id());
// If there are no stored permissions, there's nothing to adjust.
if (!permissions)
return nullptr;
// If the extension is allowed to run on chrome:// URLs, then we don't have
// to adjust anything.
if (PermissionsData::AllUrlsIncludesChromeUrls(extension.id()))
return permissions;
// We need to adjust a pattern if it matches all URLs and includes the
// chrome:-scheme. These patterns would otherwise match hosts like
// chrome://settings, which should not be allowed.
// NOTE: We don't need to adjust for the file scheme, because
// ExtensionPrefs properly does that based on the extension's file access.
auto needs_chrome_scheme_adjustment = [](const URLPattern& pattern) {
return pattern.match_all_urls() &&
((pattern.valid_schemes() & URLPattern::SCHEME_CHROMEUI) != 0);
};
// NOTE: We don't need to check scriptable_hosts, because the default
// scriptable_hosts scheme mask omits the chrome:-scheme in normal
// circumstances (whereas the default explicit scheme does not, in order to
// allow for patterns like chrome://favicon).
bool needs_adjustment = base::ranges::any_of(permissions->explicit_hosts(),
needs_chrome_scheme_adjustment);
// If no patterns need adjustment, return the original set.
if (!needs_adjustment)
return permissions;
// Otherwise, iterate over the explicit hosts, and modify any that need to be
// tweaked, adding back in permitted chrome:-scheme hosts. This logic mirrors
// that in PermissionsParser, and is also similar to logic in
// permissions_api_helpers::UnpackOriginPermissions(), and has some overlap
// to URLPatternSet::Populate().
// TODO(devlin): ^^ Ouch. Refactor so that this isn't duplicated.
URLPatternSet new_explicit_hosts;
for (const auto& pattern : permissions->explicit_hosts()) {
if (!needs_chrome_scheme_adjustment(pattern)) {
new_explicit_hosts.AddPattern(pattern);
continue;
}
URLPattern new_pattern(pattern);
int new_valid_schemes =
pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI;
new_pattern.SetValidSchemes(new_valid_schemes);
new_explicit_hosts.AddPattern(std::move(new_pattern));
}
permissions->SetExplicitHosts(std::move(new_explicit_hosts));
return permissions;
}
std::unique_ptr<PermissionSet>
PermissionsManager::GetBoundedExtensionDesiredPermissions(
const Extension& extension) const {
// Determine the extension's "required" permissions (though even these can
// be withheld).
const PermissionSet& required_permissions =
PermissionsParser::GetRequiredPermissions(&extension);
// Retrieve the desired permissions from prefs. "Desired permissions" here
// are the permissions the extension most recently set for itself. This
// might not be all granted permissions, since extensions can revoke their
// own permissions via chrome.permissions.remove() (which removes the
// permission from the active set, but not the granted set).
std::unique_ptr<PermissionSet> desired_active_permissions =
extension_prefs_->GetDesiredActivePermissions(extension.id());
// The stored desired permissions may be null if the extension has never
// used the permissions API to modify its active permissions. In this case,
// the desired permissions are simply the set of required permissions.
if (!desired_active_permissions)
return required_permissions.Clone();
// Otherwise, the extension has stored a set of desired permissions. This
// could actually be a superset *or* a subset of requested permissions by the
// extension (depending on how its permissions have changed).
// Start by calculating the set of all current potentially-desired
// permissions by combining the required and optional permissions.
std::unique_ptr<PermissionSet> requested_permissions =
PermissionSet::CreateUnion(
required_permissions,
PermissionsParser::GetOptionalPermissions(&extension));
// Now, take the intersection of the requested permissions and the stored
// permissions. This filters out any previously-stored permissions that are
// no longer used (which we continue to store in prefs in case the extension
// wants them back in the future).
std::unique_ptr<PermissionSet> bounded_desired =
PermissionSet::CreateIntersection(*desired_active_permissions,
*requested_permissions);
// Additionally, we ensure that all "required" permissions are included in
// this desired set (to guard against any pref corruption - this ensures at
// least everything is in a "sane" state).
// TODO(https://crbug.com/1341118): Maddeningly, the order of the arguments
// passed to CreateUnion() here is *important*. Passing `bounded_desired` as
// the first param results in the valid schemes being removed.
bounded_desired =
PermissionSet::CreateUnion(required_permissions, *bounded_desired);
return bounded_desired;
}
std::unique_ptr<PermissionSet>
PermissionsManager::GetEffectivePermissionsToGrant(
const Extension& extension,
const PermissionSet& desired_permissions) const {
if (!util::CanWithholdPermissionsFromExtension(extension)) {
// The withhold creation flag should never have been set in cases where
// withholding isn't allowed.
DCHECK(!(extension.creation_flags() & Extension::WITHHOLD_PERMISSIONS));
return desired_permissions.Clone();
}
if (desired_permissions.effective_hosts().is_empty()) {
return desired_permissions.Clone(); // No hosts to withhold.
}
// Determine if we should withhold host permissions. This is different for
// extensions that are being newly-installed and extensions that have already
// been installed; this is indicated by the extension creation flags.
bool should_withhold = false;
if (extension.creation_flags() & Extension::WITHHOLD_PERMISSIONS)
should_withhold = true;
else
should_withhold = HasWithheldHostPermissions(extension);
if (!should_withhold)
return desired_permissions.Clone();
// Otherwise, permissions should be withheld according to the user-granted
// permission set.
// Determine the permissions granted by the user at runtime. If none are found
// in prefs, default it to an empty set.
std::unique_ptr<PermissionSet> runtime_granted_permissions =
GetRuntimePermissionsFromPrefs(extension);
if (!runtime_granted_permissions)
runtime_granted_permissions = std::make_unique<PermissionSet>();
PermissionSet user_granted_permissions;
if (base::FeatureList::IsEnabled(
extensions_features::kExtensionsMenuAccessControl)) {
// Also add any hosts the user indicated extensions may always run on.
URLPatternSet user_allowed_sites;
for (const auto& site : user_permissions_.permitted_sites) {
user_allowed_sites.AddOrigin(Extension::kValidHostPermissionSchemes,
site);
}
user_granted_permissions =
PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
user_allowed_sites.Clone(), user_allowed_sites.Clone());
}
return GetAllowedPermissionsAfterWithholding(desired_permissions,
*runtime_granted_permissions,
user_granted_permissions);
}
std::unique_ptr<const PermissionSet>
PermissionsManager::GetRevokablePermissions(const Extension& extension) const {
// No extra revokable permissions if the extension couldn't ever be affected.
if (!util::CanWithholdPermissionsFromExtension(extension))
return nullptr;
// If we aren't withholding host permissions, then there may be some
// permissions active on the extension that should be revokable. Otherwise,
// all granted permissions should be stored in the preferences (and these
// can be a superset of permissions on the extension, as in the case of e.g.
// granting origins when only a subset is requested by the extension).
// TODO(devlin): This is confusing and subtle. We should instead perhaps just
// add all requested hosts as runtime-granted hosts if we aren't withholding
// host permissions.
const PermissionSet* current_granted_permissions = nullptr;
std::unique_ptr<const PermissionSet> runtime_granted_permissions =
GetRuntimePermissionsFromPrefs(extension);
std::unique_ptr<const PermissionSet> union_set;
if (runtime_granted_permissions) {
union_set = PermissionSet::CreateUnion(
*runtime_granted_permissions,
extension.permissions_data()->active_permissions());
current_granted_permissions = union_set.get();
} else {
current_granted_permissions =
&extension.permissions_data()->active_permissions();
}
// Unrevokable permissions include granted API permissions, manifest
// permissions, and host permissions that are always allowed.
PermissionSet unrevokable_permissions(
current_granted_permissions->apis().Clone(),
current_granted_permissions->manifest_permissions().Clone(),
URLPatternSet(), URLPatternSet());
{
// TODO(devlin): We do this pattern of "required + optional" enough. Make it
// a part of PermissionsParser and stop duplicating the set each time.
std::unique_ptr<PermissionSet> requested_permissions =
PermissionSet::CreateUnion(
PermissionsParser::GetRequiredPermissions(&extension),
PermissionsParser::GetOptionalPermissions(&extension));
ExtensionsBrowserClient::Get()->AddAdditionalAllowedHosts(
*requested_permissions, &unrevokable_permissions);
}
// Revokable permissions are, predictably, any in the current set that aren't
// considered unrevokable.
return PermissionSet::CreateDifference(*current_granted_permissions,
unrevokable_permissions);
}
void PermissionsManager::NotifyExtensionPermissionsUpdated(
const Extension& extension,
const PermissionSet& permissions,
UpdateReason reason) {
for (Observer& observer : observers_) {
observer.OnExtensionPermissionsUpdated(extension, permissions, reason);
}
}
void PermissionsManager::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void PermissionsManager::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void PermissionsManager::OnUserPermissionsSettingsChanged() {
// TODO(http://crbug.com/1268198): AddOrigin() below can fail if the
// added URLPattern doesn't parse (such as if the schemes are invalid). We
// need to make sure that origins added to this list only contain schemes that
// are valid for extensions to act upon (and gracefully handle others).
URLPatternSet user_blocked_sites;
for (const auto& site : user_permissions_.restricted_sites)
user_blocked_sites.AddOrigin(Extension::kValidHostPermissionSchemes, site);
URLPatternSet user_allowed_sites;
for (const auto& site : user_permissions_.permitted_sites)
user_allowed_sites.AddOrigin(Extension::kValidHostPermissionSchemes, site);
PermissionSet user_allowed_set(APIPermissionSet(), ManifestPermissionSet(),
user_allowed_sites.Clone(),
user_allowed_sites.Clone());
// Update all installed extensions with the new user permissions. We do this
// for all installed extensions (and not just enabled extensions) so that
// entries in the chrome://extensions page for disabled extensions are
// accurate.
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
auto all_extensions = registry->GenerateInstalledExtensionsSet();
for (const auto& extension : all_extensions) {
UpdatePermissionsWithUserSettings(*extension, user_allowed_set);
}
// Send the new permissions states to the renderers, including both the
// updated user host settings and the updated permissions for each extension.
// Unlike above, we only care about enabled extensions here, since disabled
// extensions aren't running.
{
ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get();
for (content::RenderProcessHost::iterator host_iterator(
content::RenderProcessHost::AllHostsIterator());
!host_iterator.IsAtEnd(); host_iterator.Advance()) {
content::RenderProcessHost* host = host_iterator.GetCurrentValue();
if (host->IsInitializedAndNotDead() &&
browser_client->IsSameContext(browser_context_,
host->GetBrowserContext())) {
mojom::Renderer* renderer =
RendererStartupHelperFactory::GetForBrowserContext(
host->GetBrowserContext())
->GetRenderer(host);
if (renderer) {
renderer->UpdateUserHostRestrictions(user_blocked_sites.Clone(),
user_allowed_sites.Clone());
for (const auto& extension : registry->enabled_extensions()) {
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());
}
}
}
}
}
PermissionsData::SetUserHostRestrictions(
util::GetBrowserContextId(browser_context_),
std::move(user_blocked_sites), std::move(user_allowed_sites));
// Notify observers of a permissions change once the changes have taken
// effect in the network layer.
NetworkPermissionsUpdater::UpdateAllExtensions(
*browser_context_,
base::BindOnce(&PermissionsManager::NotifyUserPermissionSettingsChanged,
weak_factory_.GetWeakPtr()));
}
bool PermissionsManager::RemovePermittedSiteAndUpdatePrefs(
const url::Origin& origin) {
bool removed_site = user_permissions_.permitted_sites.erase(origin);
if (removed_site)
RemoveSiteFromPrefs(extension_prefs_, kPermittedSites, origin);
return removed_site;
}
bool PermissionsManager::RemoveRestrictedSiteAndUpdatePrefs(
const url::Origin& origin) {
bool removed_site = user_permissions_.restricted_sites.erase(origin);
if (removed_site)
RemoveSiteFromPrefs(extension_prefs_, kRestrictedSites, origin);
return removed_site;
}
void PermissionsManager::NotifyUserPermissionSettingsChanged() {
for (auto& observer : observers_) {
observer.OnUserPermissionsSettingsChanged(GetUserPermissionsSettings());
}
}
void PermissionsManager::NotifyShowAccessRequestsInToolbarChanged(
const extensions::ExtensionId& extension_id,
bool can_show_requests) {
for (auto& observer : observers_) {
observer.OnShowAccessRequestsInToolbarChanged(extension_id,
can_show_requests);
}
}
} // namespace extensions