Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(765)

Unified Diff: extensions/common/manifest_handlers/permissions_parser.cc

Issue 309533007: Refactor PermissionsData pt1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: extensions/common/manifest_handlers/permissions_parser.cc
diff --git a/extensions/common/manifest_handlers/permissions_parser.cc b/extensions/common/manifest_handlers/permissions_parser.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f7b4b08e20927943bd3e9c09f82b585e5f9bc104
--- /dev/null
+++ b/extensions/common/manifest_handlers/permissions_parser.cc
@@ -0,0 +1,347 @@
+// Copyright 2014 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 "extensions/common/manifest_handlers/permissions_parser.h"
+
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extensions_client.h"
+#include "extensions/common/features/feature.h"
+#include "extensions/common/features/feature_provider.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handler.h"
+#include "extensions/common/permissions/api_permission_set.h"
+#include "extensions/common/permissions/permission_set.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/switches.h"
+#include "extensions/common/url_pattern_set.h"
+#include "url/url_constants.h"
+
+namespace extensions {
+
+namespace {
+
+namespace keys = manifest_keys;
+namespace errors = manifest_errors;
+
+struct ManifestPermissions : public Extension::ManifestData {
+ ManifestPermissions(scoped_refptr<const PermissionSet> permissions);
+ virtual ~ManifestPermissions();
+
+ scoped_refptr<const PermissionSet> permissions;
+};
+
+ManifestPermissions::ManifestPermissions(
+ scoped_refptr<const PermissionSet> permissions) : permissions(permissions) {
+}
+
+ManifestPermissions::~ManifestPermissions() {
+}
+
+// Custom checks for the experimental permission that can't be expressed in
+// _permission_features.json.
+bool CanSpecifyExperimentalPermission(const Extension* extension) {
+ if (extension->location() == Manifest::COMPONENT)
+ return true;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalExtensionApis)) {
+ return true;
+ }
+
+ // We rely on the webstore to check access to experimental. This way we can
+ // whitelist extensions to have access to experimental in just the store, and
+ // not have to push a new version of the client.
+ if (extension->from_webstore())
+ return true;
+
+ return false;
+}
+
+// Checks whether the host |pattern| is allowed for the given |extension|,
+// given API permissions |permissions|.
+bool CanSpecifyHostPermission(const Extension* extension,
+ const URLPattern& pattern,
+ const APIPermissionSet& permissions) {
+ if (!pattern.match_all_urls() &&
+ pattern.MatchesScheme(content::kChromeUIScheme)) {
+ URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()->
+ GetPermittedChromeSchemeHosts(extension, permissions);
+ if (chrome_scheme_hosts.ContainsPattern(pattern))
+ return true;
+
+ // Component extensions can have access to all of chrome://*.
+ if (PermissionsData::CanExecuteScriptEverywhere(extension))
+ return true;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kExtensionsOnChromeURLs)) {
+ return true;
+ }
+
+ // TODO(aboxhall): return from_webstore() when webstore handles blocking
+ // extensions which request chrome:// urls
+ return false;
+ }
+
+ // Otherwise, the valid schemes were handled by URLPattern.
+ return true;
+}
+
+// Parses the host and api permissions from the specified permission |key|
+// from |extension|'s manifest.
+bool ParseHelper(Extension* extension,
+ const char* key,
+ APIPermissionSet* api_permissions,
+ URLPatternSet* host_permissions,
+ base::string16* error) {
+ if (!extension->manifest()->HasKey(key))
+ return true;
+
+ const base::ListValue* permissions = NULL;
+ if (!extension->manifest()->GetList(key, &permissions)) {
+ *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
+ std::string());
+ return false;
+ }
+
+ // NOTE: We need to get the APIPermission before we check if features
+ // associated with them are available because the feature system does not
+ // know about aliases.
+
+ std::vector<std::string> host_data;
+ if (!APIPermissionSet::ParseFromJSON(
+ permissions, APIPermissionSet::kDisallowInternalPermissions,
+ api_permissions, error, &host_data)) {
+ return false;
+ }
+
+ // Verify feature availability of permissions.
+ std::vector<APIPermission::ID> to_remove;
+ const FeatureProvider* permission_features =
+ FeatureProvider::GetPermissionFeatures();
+ for (APIPermissionSet::const_iterator iter = api_permissions->begin();
+ iter != api_permissions->end(); ++iter) {
+ Feature* feature = permission_features->GetFeature(iter->name());
+
+ // The feature should exist since we just got an APIPermission for it. The
+ // two systems should be updated together whenever a permission is added.
+ DCHECK(feature) << "Could not find feature for " << iter->name();
+ // http://crbug.com/176381
+ if (!feature) {
+ to_remove.push_back(iter->id());
+ continue;
+ }
+
+ Feature::Availability availability =
+ feature->IsAvailableToExtension(extension);
+ if (!availability.is_available()) {
+ // Don't fail, but warn the developer that the manifest contains
+ // unrecognized permissions. This may happen legitimately if the
+ // extensions requests platform- or channel-specific permissions.
+ extension->AddInstallWarning(InstallWarning(availability.message(),
+ feature->name()));
+ to_remove.push_back(iter->id());
+ continue;
+ }
+
+ if (iter->id() == APIPermission::kExperimental) {
+ if (!CanSpecifyExperimentalPermission(extension)) {
+ *error = base::ASCIIToUTF16(errors::kExperimentalFlagRequired);
+ return false;
+ }
+ }
+ }
+
+ api_permissions->AddImpliedPermissions();
+
+ // Remove permissions that are not available to this extension.
+ for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
+ iter != to_remove.end(); ++iter) {
+ api_permissions->erase(*iter);
+ }
+
+ // Parse host pattern permissions.
+ const int kAllowedSchemes =
+ PermissionsData::CanExecuteScriptEverywhere(extension) ?
+ URLPattern::SCHEME_ALL : Extension::kValidHostPermissionSchemes;
+
+ for (std::vector<std::string>::const_iterator iter = host_data.begin();
+ iter != host_data.end(); ++iter) {
+ const std::string& permission_str = *iter;
+
+ // Check if it's a host pattern permission.
+ URLPattern pattern = URLPattern(kAllowedSchemes);
+ URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
+ if (parse_result == URLPattern::PARSE_SUCCESS) {
+ // The path component is not used for host permissions, so we force it
+ // to match all paths.
+ pattern.SetPath("/*");
+ int valid_schemes = pattern.valid_schemes();
+ if (pattern.MatchesScheme(url::kFileScheme) &&
+ !PermissionsData::CanExecuteScriptEverywhere(extension)) {
+ extension->set_wants_file_access(true);
+ if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
+ valid_schemes &= ~URLPattern::SCHEME_FILE;
+ }
+
+ if (pattern.scheme() != content::kChromeUIScheme &&
+ !PermissionsData::CanExecuteScriptEverywhere(extension)) {
+ // Keep chrome:// in allowed schemes only if it's explicitly requested
+ // or CanExecuteScriptEverywhere is true. If the
+ // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
+ // will fail, so don't check the flag here.
+ valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
+ }
+ pattern.SetValidSchemes(valid_schemes);
+
+ if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
+ // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
+ // below).
+ extension->AddInstallWarning(InstallWarning(
+ ErrorUtils::FormatErrorMessage(
+ errors::kInvalidPermissionScheme, permission_str),
+ key,
+ permission_str));
+ continue;
+ }
+
+ host_permissions->AddPattern(pattern);
+ // We need to make sure all_urls matches chrome://favicon and (maybe)
+ // chrome://thumbnail, so add them back in to host_permissions separately.
+ if (pattern.match_all_urls()) {
+ host_permissions->AddPatterns(
+ ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
+ extension, *api_permissions));
+ }
+ continue;
+ }
+
+ // It's probably an unknown API permission. Do not throw an error so
+ // extensions can retain backwards compatability (http://crbug.com/42742).
+ extension->AddInstallWarning(InstallWarning(
+ ErrorUtils::FormatErrorMessage(
+ manifest_errors::kPermissionUnknownOrMalformed,
+ permission_str),
+ key,
+ permission_str));
+ }
+
+ return true;
+}
+
+} // namespace
+
+struct PermissionsParser::InitialPermissions {
+ APIPermissionSet api_permissions;
+ ManifestPermissionSet manifest_permissions;
+ URLPatternSet host_permissions;
+ URLPatternSet scriptable_hosts;
+};
+
+PermissionsParser::PermissionsParser() {
+}
+
+PermissionsParser::~PermissionsParser() {
+}
+
+bool PermissionsParser::Parse(Extension* extension, base::string16* error) {
+ initial_required_permissions_.reset(new InitialPermissions);
+ if (!ParseHelper(extension,
+ keys::kPermissions,
+ &initial_required_permissions_->api_permissions,
+ &initial_required_permissions_->host_permissions,
+ error)) {
+ return false;
+ }
+
+ initial_optional_permissions_.reset(new InitialPermissions);
+ if (!ParseHelper(extension,
+ keys::kOptionalPermissions,
+ &initial_optional_permissions_->api_permissions,
+ &initial_optional_permissions_->host_permissions,
+ error)) {
+ return false;
+ }
+
+ return true;
+}
+
+void PermissionsParser::Finalize(Extension* extension) {
+ ManifestHandler::AddExtensionInitialRequiredPermissions(
+ extension, &initial_required_permissions_->manifest_permissions);
+
+ scoped_refptr<const PermissionSet> required_permissions(new PermissionSet(
+ initial_required_permissions_->api_permissions,
+ initial_required_permissions_->manifest_permissions,
+ initial_required_permissions_->host_permissions,
+ initial_required_permissions_->scriptable_hosts));
+ extension->SetManifestData(keys::kPermissions,
+ new ManifestPermissions(required_permissions));
+
+ scoped_refptr<const PermissionSet> optional_permissions(new PermissionSet(
+ initial_optional_permissions_->api_permissions,
+ initial_optional_permissions_->manifest_permissions,
+ initial_optional_permissions_->host_permissions,
+ URLPatternSet()));
+ extension->SetManifestData(keys::kOptionalPermissions,
+ new ManifestPermissions(optional_permissions));
+}
+
+// static
+void PermissionsParser::AddAPIPermission(Extension* extension,
+ APIPermission::ID permission) {
+ DCHECK(extension->permissions_parser());
+ extension->permissions_parser()->initial_required_permissions_
+ ->api_permissions.insert(permission);
+
+}
+
+// static
+void PermissionsParser::AddAPIPermission(Extension* extension,
+ APIPermission* permission) {
+ DCHECK(extension->permissions_parser());
+ extension->permissions_parser()->initial_required_permissions_
+ ->api_permissions.insert(permission);
+}
+
+// static
+bool PermissionsParser::HasAPIPermission(const Extension* extension,
+ APIPermission::ID permission) {
+ DCHECK(extension->permissions_parser());
+ return extension->permissions_parser()
+ ->initial_required_permissions_->api_permissions.count(permission) > 0;
+}
+
+// static
+void PermissionsParser::SetScriptableHosts(
+ Extension* extension, const URLPatternSet& scriptable_hosts) {
+ DCHECK(extension->permissions_parser());
+ extension->permissions_parser()->initial_required_permissions_
+ ->scriptable_hosts = scriptable_hosts;
+}
+
+// static
+scoped_refptr<const PermissionSet> PermissionsParser::GetRequiredPermissions(
+ const Extension* extension) {
+ DCHECK(extension->GetManifestData(keys::kPermissions));
+ return static_cast<const ManifestPermissions*>(
+ extension->GetManifestData(keys::kPermissions))->permissions;
+}
+
+// static
+scoped_refptr<const PermissionSet> PermissionsParser::GetOptionalPermissions(
+ const Extension* extension) {
+ DCHECK(extension->GetManifestData(keys::kOptionalPermissions));
+ return static_cast<const ManifestPermissions*>(
+ extension->GetManifestData(keys::kOptionalPermissions))->permissions;
+}
+
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698