diff options
author | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 15:55:24 +0000 |
---|---|---|
committer | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 15:55:24 +0000 |
commit | e737c445ac38ebf9427f2590e8d29034c68793f6 (patch) | |
tree | 87eccc6072df7cfe3a0d38475732ec99d9a4e1c0 | |
parent | d4605c119ecb054772ff1943dcba2f3f8e0af363 (diff) | |
download | chromium_src-e737c445ac38ebf9427f2590e8d29034c68793f6.zip chromium_src-e737c445ac38ebf9427f2590e8d29034c68793f6.tar.gz chromium_src-e737c445ac38ebf9427f2590e8d29034c68793f6.tar.bz2 |
Enable permission warnings from ManifestHandlers.
BUG=293755,247857
Review URL: https://codereview.chromium.org/51433002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235332 0039d316-1c4b-4281-b951-d872f2087c98
51 files changed, 2108 insertions, 626 deletions
diff --git a/chrome/browser/extensions/active_tab_permission_granter.cc b/chrome/browser/extensions/active_tab_permission_granter.cc index d8c0ce6..aebeb39 100644 --- a/chrome/browser/extensions/active_tab_permission_granter.cc +++ b/chrome/browser/extensions/active_tab_permission_granter.cc @@ -59,7 +59,8 @@ void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) { if (!new_apis.empty() || !new_hosts.is_empty()) { granted_extensions_.Insert(extension); scoped_refptr<const PermissionSet> new_permissions = - new PermissionSet(new_apis, new_hosts, URLPatternSet()); + new PermissionSet(new_apis, ManifestPermissionSet(), + new_hosts, URLPatternSet()); PermissionsData::UpdateTabSpecificPermissions(extension, tab_id_, new_permissions); diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc index 620c8c5..2b90287 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers.cc @@ -56,6 +56,9 @@ scoped_ptr<Permissions> PackPermissionSet(const PermissionSet* set) { } } + // TODO(rpaquay): We currently don't expose manifest permissions + // to apps/extensions via the permissions API. + permissions->origins.reset(new std::vector<std::string>()); URLPatternSet hosts = set->explicit_hosts(); for (URLPatternSet::const_iterator i = hosts.begin(); i != hosts.end(); ++i) @@ -121,6 +124,10 @@ scoped_refptr<PermissionSet> UnpackPermissionSet( } } + // TODO(rpaquay): We currently don't expose manifest permissions + // to apps/extensions via the permissions API. + ManifestPermissionSet manifest_permissions; + URLPatternSet origins; if (permissions.origins.get()) { for (std::vector<std::string>::iterator it = permissions.origins->begin(); @@ -142,7 +149,7 @@ scoped_refptr<PermissionSet> UnpackPermissionSet( } return scoped_refptr<PermissionSet>( - new PermissionSet(apis, origins, URLPatternSet())); + new PermissionSet(apis, manifest_permissions, origins, URLPatternSet())); } } // namespace permissions_api_helpers diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc index fe3e860..4ac1b81 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc @@ -37,7 +37,7 @@ TEST(ExtensionPermissionsAPIHelpers, Pack) { AddPattern(&hosts, "http://b.com/*"); scoped_refptr<PermissionSet> permission_set = - new PermissionSet(apis, hosts, URLPatternSet()); + new PermissionSet(apis, ManifestPermissionSet(), hosts, URLPatternSet()); // Pack the permission set to value and verify its contents. scoped_ptr<Permissions> permissions(PackPermissionSet(permission_set.get())); diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc index 736112f..e3e1552 100644 --- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc @@ -72,10 +72,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, OptionalPermissionsGranted) { // Mark all the tested APIs as granted to bypass the confirmation UI. APIPermissionSet apis; apis.insert(APIPermission::kBookmark); + ManifestPermissionSet manifest_permissions; URLPatternSet explicit_hosts; AddPattern(&explicit_hosts, "http://*.c.com/*"); scoped_refptr<PermissionSet> granted_permissions = - new PermissionSet(apis, explicit_hosts, URLPatternSet()); + new PermissionSet(apis, manifest_permissions, + explicit_hosts, URLPatternSet()); ExtensionPrefs* prefs = browser()->profile()->GetExtensionService()->extension_prefs(); diff --git a/chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.cc b/chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.cc index 729587f..fbf02dc 100644 --- a/chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.cc +++ b/chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.cc @@ -6,7 +6,7 @@ #include "chrome/browser/extensions/api/socket/tcp_socket.h" #include "chrome/browser/extensions/api/sockets_tcp/tcp_socket_event_dispatcher.h" -#include "chrome/common/extensions/api/sockets/sockets_handler.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" #include "content/public/common/socket_permission_request.h" #include "net/base/net_errors.h" diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc index cace238..e914a5a 100644 --- a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc +++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc @@ -6,7 +6,7 @@ #include "chrome/browser/extensions/api/socket/tcp_socket.h" #include "chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" -#include "chrome/common/extensions/api/sockets/sockets_handler.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" #include "chrome/common/extensions/permissions/permissions_data.h" #include "chrome/common/extensions/permissions/socket_permission.h" #include "content/public/common/socket_permission_request.h" diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc index 5939c44..fd8101c 100644 --- a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc @@ -6,7 +6,7 @@ #include "chrome/browser/extensions/api/socket/udp_socket.h" #include "chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h" -#include "chrome/common/extensions/api/sockets/sockets_handler.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" #include "content/public/common/socket_permission_request.h" #include "net/base/net_errors.h" diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index f021de4..d6394e8 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -157,6 +157,7 @@ const char kPrefGrantedPermissions[] = "granted_permissions"; // The preference names for PermissionSet values. const char kPrefAPIs[] = "api"; +const char kPrefManifestPermissions[] = "manifest_permissions"; const char kPrefExplicitHosts[] = "explicit_host"; const char kPrefScriptableHosts[] = "scriptable_host"; @@ -545,6 +546,18 @@ PermissionSet* ExtensionPrefs::ReadPrefAsPermissionSet( &apis, NULL, NULL); } + // Retrieve the Manifest Keys permissions. Please refer to + // |SetExtensionPrefPermissionSet| for manifest_permissions_values format. + ManifestPermissionSet manifest_permissions; + const ListValue* manifest_permissions_values = NULL; + std::string manifest_permission_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + if (ReadPrefAsList(extension_id, manifest_permission_pref, + &manifest_permissions_values)) { + ManifestPermissionSet::ParseFromJSON( + manifest_permissions_values, &manifest_permissions, NULL, NULL); + } + // Retrieve the explicit host permissions. URLPatternSet explicit_hosts; ReadPrefAsURLPatternSet( @@ -557,37 +570,51 @@ PermissionSet* ExtensionPrefs::ReadPrefAsPermissionSet( extension_id, JoinPrefs(pref_key, kPrefScriptableHosts), &scriptable_hosts, UserScript::ValidUserScriptSchemes()); - return new PermissionSet(apis, explicit_hosts, scriptable_hosts); -} - -void ExtensionPrefs::SetExtensionPrefPermissionSet( - const std::string& extension_id, - const std::string& pref_key, - const PermissionSet* new_value) { - // Set the API permissions. - // The format of api_values is: - // [ "permission_name1", // permissions do not support detail. - // "permission_name2", - // {"permission_name3": value }, - // // permission supports detail, permission detail will be stored in value. - // ... - // ] - ListValue* api_values = new ListValue(); - APIPermissionSet apis = new_value->apis(); - std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); - for (APIPermissionSet::const_iterator i = apis.begin(); - i != apis.end(); ++i) { + return new PermissionSet( + apis, manifest_permissions, explicit_hosts, scriptable_hosts); +} + +// Set the API or Manifest permissions. +// The format of api_values is: +// [ "permission_name1", // permissions do not support detail. +// "permission_name2", +// {"permission_name3": value }, +// // permission supports detail, permission detail will be stored in value. +// ... +// ] +template<typename T> +static ListValue* CreatePermissionList(const T& permissions) { + ListValue* values = new ListValue(); + for (typename T::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) { scoped_ptr<Value> detail(i->ToValue()); if (detail) { DictionaryValue* tmp = new DictionaryValue(); tmp->Set(i->name(), detail.release()); - api_values->Append(tmp); + values->Append(tmp); } else { - api_values->Append(new base::StringValue(i->name())); + values->Append(new base::StringValue(i->name())); } } + return values; +} + +void ExtensionPrefs::SetExtensionPrefPermissionSet( + const std::string& extension_id, + const std::string& pref_key, + const PermissionSet* new_value) { + std::string api_pref = JoinPrefs(pref_key, kPrefAPIs); + ListValue* api_values = CreatePermissionList(new_value->apis()); UpdateExtensionPref(extension_id, api_pref, api_values); + std::string manifest_permissions_pref = + JoinPrefs(pref_key, kPrefManifestPermissions); + ListValue* manifest_permissions_values = CreatePermissionList( + new_value->manifest_permissions()); + UpdateExtensionPref(extension_id, + manifest_permissions_pref, + manifest_permissions_values); + // Set the explicit host permissions. if (!new_value->explicit_hosts().is_empty()) { SetExtensionPrefURLPatternSet(extension_id, diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index 511aeed..3d1d24f 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -222,6 +222,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { AddPattern(&shost_permissions_, "http://example.com/*"); APIPermissionSet empty_set; + ManifestPermissionSet empty_manifest_permissions; URLPatternSet empty_extent; scoped_refptr<PermissionSet> permissions; scoped_refptr<PermissionSet> granted_permissions; @@ -232,7 +233,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { EXPECT_TRUE(granted_permissions->IsEmpty()); permissions = new PermissionSet( - api_perm_set1_, empty_extent, empty_extent); + api_perm_set1_, empty_manifest_permissions, empty_extent, empty_extent); // Add part of the api permissions. prefs()->AddGrantedPermissions(extension_id_, permissions.get()); @@ -246,7 +247,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { // Add part of the explicit host permissions. permissions = new PermissionSet( - empty_set, ehost_perm_set1_, empty_extent); + empty_set, empty_manifest_permissions, ehost_perm_set1_, empty_extent); prefs()->AddGrantedPermissions(extension_id_, permissions.get()); granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_FALSE(granted_permissions->IsEmpty()); @@ -259,7 +260,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { // Add part of the scriptable host permissions. permissions = new PermissionSet( - empty_set, empty_extent, shost_perm_set1_); + empty_set, empty_manifest_permissions, empty_extent, shost_perm_set1_); prefs()->AddGrantedPermissions(extension_id_, permissions.get()); granted_permissions = prefs()->GetGrantedPermissions(extension_id_); EXPECT_FALSE(granted_permissions->IsEmpty()); @@ -276,7 +277,8 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { // Add the rest of the permissions. permissions = new PermissionSet( - api_perm_set2_, ehost_perm_set2_, shost_perm_set2_); + api_perm_set2_, empty_manifest_permissions, + ehost_perm_set2_, shost_perm_set2_); APIPermissionSet::Union(expected_apis, api_perm_set2_, &api_permissions_); @@ -334,6 +336,8 @@ class ExtensionPrefsActivePermissions : public ExtensionPrefsTest { api_perms.insert(APIPermission::kBookmark); api_perms.insert(APIPermission::kHistory); + ManifestPermissionSet empty_manifest_permissions; + URLPatternSet ehosts; AddPattern(&ehosts, "http://*.google.com/*"); AddPattern(&ehosts, "http://example.com/*"); @@ -343,7 +347,8 @@ class ExtensionPrefsActivePermissions : public ExtensionPrefsTest { AddPattern(&shosts, "https://*.google.com/*"); AddPattern(&shosts, "http://reddit.com/r/test/*"); - active_perms_ = new PermissionSet(api_perms, ehosts, shosts); + active_perms_ = new PermissionSet( + api_perms, empty_manifest_permissions, ehosts, shosts); // Make sure the active permissions start empty. scoped_refptr<PermissionSet> active( diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc index f8a928e..1e38cb8 100644 --- a/chrome/browser/extensions/permissions_updater.cc +++ b/chrome/browser/extensions/permissions_updater.cc @@ -138,13 +138,16 @@ void PermissionsUpdater::NotifyPermissionsUpdated( !i.IsAtEnd(); i.Advance()) { RenderProcessHost* host = i.GetCurrentValue(); Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); - if (profile_->IsSameProfile(profile)) - host->Send(new ExtensionMsg_UpdatePermissions( - static_cast<int>(reason), - extension->id(), - changed->apis(), - changed->explicit_hosts(), - changed->scriptable_hosts())); + if (profile_->IsSameProfile(profile)) { + ExtensionMsg_UpdatePermissions_Params info; + info.reason_id = static_cast<int>(reason); + info.extension_id = extension->id(); + info.apis = changed->apis(); + info.manifest_permissions = changed->manifest_permissions(); + info.explicit_hosts = changed->explicit_hosts(); + info.scriptable_hosts = changed->scriptable_hosts(); + host->Send(new ExtensionMsg_UpdatePermissions(info)); + } } // Trigger the onAdded and onRemoved events in the extension. diff --git a/chrome/browser/extensions/permissions_updater_unittest.cc b/chrome/browser/extensions/permissions_updater_unittest.cc index 30d71dc..7a0e431 100644 --- a/chrome/browser/extensions/permissions_updater_unittest.cc +++ b/chrome/browser/extensions/permissions_updater_unittest.cc @@ -119,10 +119,13 @@ TEST_F(PermissionsUpdaterTest, AddAndRemovePermissions) { APIPermissionSet default_apis; default_apis.insert(APIPermission::kManagement); + ManifestPermissionSet empty_manifest_permissions; + URLPatternSet default_hosts; AddPattern(&default_hosts, "http://a.com/*"); scoped_refptr<PermissionSet> default_permissions = - new PermissionSet(default_apis, default_hosts, URLPatternSet()); + new PermissionSet(default_apis, empty_manifest_permissions, + default_hosts, URLPatternSet()); // Make sure it loaded properly. scoped_refptr<const PermissionSet> permissions = @@ -138,7 +141,8 @@ TEST_F(PermissionsUpdaterTest, AddAndRemovePermissions) { AddPattern(&hosts, "http://*.c.com/*"); scoped_refptr<PermissionSet> delta = - new PermissionSet(apis, hosts, URLPatternSet()); + new PermissionSet(apis, empty_manifest_permissions, + hosts, URLPatternSet()); PermissionsUpdaterListener listener; PermissionsUpdater updater(profile_.get()); @@ -175,7 +179,8 @@ TEST_F(PermissionsUpdaterTest, AddAndRemovePermissions) { // In the second part of the test, we'll remove the permissions that we // just added except for 'notification'. apis.erase(APIPermission::kNotification); - delta = new PermissionSet(apis, hosts, URLPatternSet()); + delta = new PermissionSet(apis, empty_manifest_permissions, + hosts, URLPatternSet()); listener.Reset(); updater.RemovePermissions(extension.get(), delta.get()); diff --git a/chrome/browser/themes/theme_syncable_service_unittest.cc b/chrome/browser/themes/theme_syncable_service_unittest.cc index 82e721b..b95adbe 100644 --- a/chrome/browser/themes/theme_syncable_service_unittest.cc +++ b/chrome/browser/themes/theme_syncable_service_unittest.cc @@ -207,9 +207,11 @@ class ThemeSyncableServiceTest : public testing::Test { GetThemeLocation(), kCustomThemeUrl); extensions::APIPermissionSet empty_set; + extensions::ManifestPermissionSet empty_manifest_permissions; extensions::URLPatternSet empty_extent; scoped_refptr<extensions::PermissionSet> permissions = - new extensions::PermissionSet(empty_set, empty_extent, empty_extent); + new extensions::PermissionSet(empty_set, empty_manifest_permissions, + empty_extent, empty_extent); service->extension_prefs()->AddGrantedPermissions( theme_extension_->id(), permissions.get()); service->AddExtension(theme_extension_.get()); diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index dbd07b6..1f0f67f 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -145,8 +145,12 @@ 'common/extensions/api/omnibox/omnibox_handler.h', 'common/extensions/api/plugins/plugins_handler.cc', 'common/extensions/api/plugins/plugins_handler.h', - 'common/extensions/api/sockets/sockets_handler.cc', - 'common/extensions/api/sockets/sockets_handler.h', + 'common/extensions/api/sockets/sockets_manifest_handler.cc', + 'common/extensions/api/sockets/sockets_manifest_handler.h', + 'common/extensions/api/sockets/sockets_manifest_data.cc', + 'common/extensions/api/sockets/sockets_manifest_data.h', + 'common/extensions/api/sockets/sockets_manifest_permission.cc', + 'common/extensions/api/sockets/sockets_manifest_permission.h', 'common/extensions/api/speech/tts_engine_manifest_handler.cc', 'common/extensions/api/speech/tts_engine_manifest_handler.h', 'common/extensions/api/spellcheck/spellcheck_handler.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index b86305f..b648e4c 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -540,6 +540,7 @@ '../extensions/common/matcher/url_matcher_unittest.cc', '../extensions/common/one_shot_event_unittest.cc', '../extensions/common/permissions/api_permission_set_unittest.cc', + '../extensions/common/permissions/manifest_permission_set_unittest.cc', '../extensions/common/url_pattern_set_unittest.cc', '../extensions/common/url_pattern_unittest.cc', '../extensions/common/user_script_unittest.cc', @@ -1800,6 +1801,7 @@ 'common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc', 'common/extensions/api/i18n/default_locale_manifest_unittest.cc', 'common/extensions/api/identity/extension_manifests_auth_unittest.cc', + 'common/extensions/api/sockets/sockets_manifest_permission_unittest.cc', 'common/extensions/api/storage/storage_schema_manifest_handler_unittest.cc', 'common/extensions/command_unittest.cc', 'common/extensions/csp_validator_unittest.cc', diff --git a/chrome/common/extensions/api/sockets/sockets_handler.cc b/chrome/common/extensions/api/sockets/sockets_handler.cc deleted file mode 100644 index a1b51c2..0000000 --- a/chrome/common/extensions/api/sockets/sockets_handler.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013 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/common/extensions/api/sockets/sockets_handler.h" - -#include "base/memory/scoped_ptr.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "chrome/common/extensions/api/manifest_types.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/permissions/permissions_data.h" -#include "chrome/common/extensions/permissions/socket_permission_data.h" -#include "extensions/common/error_utils.h" -#include "extensions/common/manifest_constants.h" -#include "extensions/common/permissions/api_permission_set.h" - -namespace extensions { - -namespace sockets_errors { -const char kErrorInvalidHostPattern[] = "Invalid host:port pattern '*'"; -} - -namespace keys = extensions::manifest_keys; -namespace errors = sockets_errors; -using api::manifest_types::Sockets; - -SocketsHandler::SocketsHandler() {} - -SocketsHandler::~SocketsHandler() {} - -bool SocketsHandler::Parse(Extension* extension, string16* error) { - const base::Value* sockets = NULL; - CHECK(extension->manifest()->Get(keys::kSockets, &sockets)); - std::vector<InstallWarning> install_warnings; - scoped_ptr<SocketsManifestData> data = - SocketsManifestData::FromValue(*sockets, - &install_warnings, - error); - if (!data) - return false; - - extension->AddInstallWarnings(install_warnings); - extension->SetManifestData(keys::kSockets, data.release()); - return true; -} - -const std::vector<std::string> SocketsHandler::Keys() const { - return SingleKey(manifest_keys::kSockets); -} - -SocketsManifestData::SocketsManifestData() {} -SocketsManifestData::~SocketsManifestData() {} - -// static -SocketsManifestData* SocketsManifestData::Get(const Extension* extension) { - return static_cast<SocketsManifestData*>( - extension->GetManifestData(keys::kSockets)); -} - -// static -bool SocketsManifestData::CheckRequest( - const Extension* extension, - const content::SocketPermissionRequest& request) { - SocketsManifestData* data = SocketsManifestData::Get(extension); - if (data == NULL) - return false; - - return data->CheckRequestImpl(extension, request); -} - -// static -scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( - const base::Value& value, - std::vector<InstallWarning>* install_warnings, - string16* error) { - scoped_ptr<Sockets> sockets = Sockets::FromValue(value, error); - if (!sockets) - return scoped_ptr<SocketsManifestData>(); - - scoped_ptr<SocketsManifestData> result(new SocketsManifestData()); - if (sockets->udp) { - if (!ParseHostPattern(result.get(), - content::SocketPermissionRequest::UDP_BIND, - sockets->udp->bind, - error)) { - return scoped_ptr<SocketsManifestData>(); - } - if (!ParseHostPattern(result.get(), - content::SocketPermissionRequest::UDP_SEND_TO, - sockets->udp->send, - error)) { - return scoped_ptr<SocketsManifestData>(); - } - if (!ParseHostPattern(result.get(), - content::SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, - sockets->udp->multicast_membership, - error)) { - return scoped_ptr<SocketsManifestData>(); - } - } - if (sockets->tcp) { - if (!ParseHostPattern(result.get(), - content::SocketPermissionRequest::TCP_CONNECT, - sockets->tcp->connect, - error)) { - return scoped_ptr<SocketsManifestData>(); - } - } - if (sockets->tcp_server) { - if (!ParseHostPattern(result.get(), - content::SocketPermissionRequest::TCP_LISTEN, - sockets->tcp_server->listen, - error)) { - return scoped_ptr<SocketsManifestData>(); - } - } - return result.Pass(); -} - -// static -bool SocketsManifestData::ParseHostPattern( - SocketsManifestData* manifest_data, - content::SocketPermissionRequest::OperationType operation_type, - const scoped_ptr<std::string>& value, - string16* error) { - if (value) { - SocketPermissionEntry entry; - if (!SocketPermissionEntry::ParseHostPattern( - operation_type, *value, &entry)) { - *error = ErrorUtils::FormatErrorMessageUTF16( - errors::kErrorInvalidHostPattern, *value); - return false; - } - manifest_data->AddPermission(entry); - } - return true; -} - -void SocketsManifestData::AddPermission(const SocketPermissionEntry& entry) { - permissions_.insert(entry); -} - -bool SocketsManifestData::CheckRequestImpl( - const Extension* extension, - const content::SocketPermissionRequest& request) { - for (PermissionSet::const_iterator it = permissions_.begin(); - it != permissions_.end(); ++it) { - if (it->Check(request)) - return true; - } - return false; -} - -} // namespace extensions diff --git a/chrome/common/extensions/api/sockets/sockets_handler.h b/chrome/common/extensions/api/sockets/sockets_handler.h deleted file mode 100644 index 2859407..0000000 --- a/chrome/common/extensions/api/sockets/sockets_handler.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2013 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. - -#ifndef CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ -#define CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ - -#include "base/strings/string16.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/permissions/socket_permission_data.h" -#include "extensions/common/manifest_handler.h" - -namespace extensions { - -// Parses the "sockets" manifest key. -class SocketsHandler : public ManifestHandler { - public: - SocketsHandler(); - virtual ~SocketsHandler(); - - virtual bool Parse(Extension* extension, string16* error) OVERRIDE; - - private: - virtual const std::vector<std::string> Keys() const OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(SocketsHandler); -}; - -// The parsed form of the "sockets" manifest entry. -class SocketsManifestData : public Extension::ManifestData { - public: - SocketsManifestData(); - virtual ~SocketsManifestData(); - - // Gets the ExternallyConnectableInfo for |extension|, or NULL if none was - // specified. - static SocketsManifestData* Get(const Extension* extension); - - static bool CheckRequest(const Extension* extension, - const content::SocketPermissionRequest& request); - - // Tries to construct the info based on |value|, as it would have appeared in - // the manifest. Sets |error| and returns an empty scoped_ptr on failure. - static scoped_ptr<SocketsManifestData> FromValue( - const base::Value& value, - std::vector<InstallWarning>* install_warnings, - string16* error); - - private: - typedef std::set<SocketPermissionEntry> PermissionSet; - - static bool ParseHostPattern( - SocketsManifestData* manifest_data, - content::SocketPermissionRequest::OperationType operation_type, - const scoped_ptr<std::string>& value, - string16* error); - - void AddPermission(const SocketPermissionEntry& entry); - - bool CheckRequestImpl(const Extension* extension, - const content::SocketPermissionRequest& request); - - PermissionSet permissions_; -}; - -} // namespace extensions - -#endif // CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_HANDLER_H_ diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_data.cc b/chrome/common/extensions/api/sockets/sockets_manifest_data.cc new file mode 100644 index 0000000..b00c8e0 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_data.cc @@ -0,0 +1,51 @@ +// Copyright 2013 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/common/extensions/api/sockets/sockets_manifest_data.h" + +#include "chrome/common/extensions/api/sockets/sockets_manifest_permission.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +SocketsManifestData::SocketsManifestData( + scoped_ptr<SocketsManifestPermission> permission) + : permission_(permission.Pass()) { + DCHECK(permission_); +} + +SocketsManifestData::~SocketsManifestData() {} + +// static +SocketsManifestData* SocketsManifestData::Get( + const Extension* extension) { + return static_cast<SocketsManifestData*>( + extension->GetManifestData(manifest_keys::kSockets)); +} + +// static +bool SocketsManifestData::CheckRequest( + const Extension* extension, + const content::SocketPermissionRequest& request) { + const SocketsManifestData* data = SocketsManifestData::Get(extension); + if (data) + return data->permission()->CheckRequest(extension, request); + + return false; +} + +// static +scoped_ptr<SocketsManifestData> SocketsManifestData::FromValue( + const base::Value& value, + string16* error) { + scoped_ptr<SocketsManifestPermission> permission = + SocketsManifestPermission::FromValue(value, error); + if (!permission) + return scoped_ptr<SocketsManifestData>(); + + return scoped_ptr<SocketsManifestData>( + new SocketsManifestData(permission.Pass())).Pass(); +} + +} // namespace extensions diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_data.h b/chrome/common/extensions/api/sockets/sockets_manifest_data.h new file mode 100644 index 0000000..b220617 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_data.h @@ -0,0 +1,54 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_DATA_H_ +#define CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_DATA_H_ + +#include <vector> + +#include "base/strings/string16.h" +#include "chrome/common/extensions/extension.h" +#include "extensions/common/manifest_handler.h" + +namespace content { +struct SocketPermissionRequest; +} + +namespace extensions { +class SocketsManifestPermission; +} + +namespace extensions { + +// The parsed form of the "sockets" manifest entry. +class SocketsManifestData : public Extension::ManifestData { + public: + explicit SocketsManifestData( + scoped_ptr<SocketsManifestPermission> permission); + virtual ~SocketsManifestData(); + + // Gets the SocketsManifestData for |extension|, or NULL if none was + // specified. + static SocketsManifestData* Get(const Extension* extension); + + static bool CheckRequest(const Extension* extension, + const content::SocketPermissionRequest& request); + + // Tries to construct the info based on |value|, as it would have appeared in + // the manifest. Sets |error| and returns an empty scoped_ptr on failure. + static scoped_ptr<SocketsManifestData> FromValue( + const base::Value& value, + string16* error); + + const SocketsManifestPermission* permission() const { + return permission_.get(); + } + + private: + scoped_ptr<SocketsManifestPermission> permission_; +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_DATA_H_ diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_handler.cc b/chrome/common/extensions/api/sockets/sockets_manifest_handler.cc new file mode 100644 index 0000000..2e57ddc --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_handler.cc @@ -0,0 +1,46 @@ +// Copyright 2013 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/common/extensions/api/sockets/sockets_manifest_handler.h" + +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_permission.h" +#include "chrome/common/extensions/extension.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +SocketsManifestHandler::SocketsManifestHandler() {} + +SocketsManifestHandler::~SocketsManifestHandler() {} + +bool SocketsManifestHandler::Parse(Extension* extension, string16* error) { + const base::Value* sockets = NULL; + CHECK(extension->manifest()->Get(manifest_keys::kSockets, &sockets)); + scoped_ptr<SocketsManifestData> data = + SocketsManifestData::FromValue(*sockets, error); + if (!data) + return false; + + extension->SetManifestData(manifest_keys::kSockets, data.release()); + return true; +} + +ManifestPermission* SocketsManifestHandler::CreatePermission() { + return new SocketsManifestPermission(); +} + +ManifestPermission* SocketsManifestHandler::CreateInitialRequiredPermission( + const Extension* extension) { + SocketsManifestData* data = SocketsManifestData::Get(extension); + if (data) + return data->permission()->Clone(); + return NULL; +} + +const std::vector<std::string> SocketsManifestHandler::Keys() const { + return SingleKey(manifest_keys::kSockets); +} + +} // namespace extensions diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_handler.h b/chrome/common/extensions/api/sockets/sockets_manifest_handler.h new file mode 100644 index 0000000..17dc401 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_handler.h @@ -0,0 +1,41 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_HANDLER_H_ +#define CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_HANDLER_H_ + +#include <string> +#include <vector> + +#include "extensions/common/manifest_handler.h" + +namespace extensions { +class Extension; +class ManifestPermission; +} + +namespace extensions { + +// Parses the "sockets" manifest key. +class SocketsManifestHandler : public ManifestHandler { + public: + SocketsManifestHandler(); + virtual ~SocketsManifestHandler(); + + // ManifestHandler overrides. + virtual bool Parse(Extension* extension, string16* error) OVERRIDE; + virtual ManifestPermission* CreatePermission() OVERRIDE; + virtual ManifestPermission* CreateInitialRequiredPermission( + const Extension* extension) OVERRIDE; + + private: + // ManifestHandler overrides. + virtual const std::vector<std::string> Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(SocketsManifestHandler); +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_HANDLER_H_ diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_permission.cc b/chrome/common/extensions/api/sockets/sockets_manifest_permission.cc new file mode 100644 index 0000000..b654d38 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_permission.cc @@ -0,0 +1,386 @@ +// Copyright 2013 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/common/extensions/api/sockets/sockets_manifest_permission.h" + +#include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/common/extensions/api/manifest_types.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" +#include "chrome/common/extensions/extension_messages.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest_constants.h" +#include "grit/generated_resources.h" +#include "ipc/ipc_message.h" +#include "ui/base/l10n/l10n_util.h" + +namespace extensions { + +namespace sockets_errors { +const char kErrorInvalidHostPattern[] = "Invalid host:port pattern '*'"; +} + +namespace errors = sockets_errors; +using api::manifest_types::Sockets; +using content::SocketPermissionRequest; + +SocketsManifestPermission::SocketsManifestPermission() + : kinds_(kNone) { +} + +SocketsManifestPermission::~SocketsManifestPermission() {} + +// static +scoped_ptr<SocketsManifestPermission> SocketsManifestPermission::FromValue( + const base::Value& value, + string16* error) { + scoped_ptr<Sockets> sockets = Sockets::FromValue(value, error); + if (!sockets) + return scoped_ptr<SocketsManifestPermission>(); + + scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission()); + if (sockets->udp) { + result->kinds_ |= kUdpPermission; + if (!ParseHostPattern(result.get(), + SocketPermissionRequest::UDP_BIND, + sockets->udp->bind, + error)) { + return scoped_ptr<SocketsManifestPermission>(); + } + if (!ParseHostPattern(result.get(), + SocketPermissionRequest::UDP_SEND_TO, + sockets->udp->send, + error)) { + return scoped_ptr<SocketsManifestPermission>(); + } + if (!ParseHostPattern(result.get(), + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + sockets->udp->multicast_membership, + error)) { + return scoped_ptr<SocketsManifestPermission>(); + } + } + if (sockets->tcp) { + result->kinds_ |= kTcpPermission; + if (!ParseHostPattern(result.get(), + SocketPermissionRequest::TCP_CONNECT, + sockets->tcp->connect, + error)) { + return scoped_ptr<SocketsManifestPermission>(); + } + } + if (sockets->tcp_server) { + result->kinds_ |= kTcpServerPermission; + if (!ParseHostPattern(result.get(), + SocketPermissionRequest::TCP_LISTEN, + sockets->tcp_server->listen, + error)) { + return scoped_ptr<SocketsManifestPermission>(); + } + } + return result.Pass(); +} + +bool SocketsManifestPermission::CheckRequest( + const Extension* extension, + const SocketPermissionRequest& request) const { + for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->Check(request)) + return true; + } + return false; +} + +std::string SocketsManifestPermission::name() const { + return manifest_keys::kSockets; +} + +std::string SocketsManifestPermission::id() const { + return name(); +} + +bool SocketsManifestPermission::HasMessages() const { + bool is_empty = permissions_.empty() && (kinds_ == kNone); + return !is_empty; +} + +PermissionMessages SocketsManifestPermission::GetMessages() const { + // TODO(rpaquay): This function and callees is (almost) a copy/paste + // from extensions::SocketPermissiona. + PermissionMessages result; + if (!AddAnyHostMessage(result)) { + AddSpecificHostMessage(result); + AddSubdomainHostMessage(result); + } + AddNetworkListMessage(result); + return result; +} + +bool SocketsManifestPermission::FromValue(const base::Value* value) { + if (!value) + return false; + string16 error; + scoped_ptr<SocketsManifestPermission> data( + SocketsManifestPermission::FromValue(*value, &error)); + + if (!data) + return false; + + permissions_ = data->permissions_; + kinds_ = data->kinds_; + return true; +} + +scoped_ptr<base::Value> SocketsManifestPermission::ToValue() const { + Sockets sockets; + if (has_udp()) { + sockets.udp.reset(new Sockets::Udp()); + sockets.udp->bind = CreateHostPattern(SocketPermissionRequest::UDP_BIND); + sockets.udp->send = CreateHostPattern(SocketPermissionRequest::UDP_SEND_TO); + sockets.udp->multicast_membership = + CreateHostPattern(SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP); + } + if (has_tcp()) { + sockets.tcp.reset(new Sockets::Tcp()); + sockets.tcp->connect = + CreateHostPattern(SocketPermissionRequest::TCP_CONNECT); + } + if (has_tcp_server()) { + sockets.tcp_server.reset(new Sockets::TcpServer()); + sockets.tcp_server->listen = + CreateHostPattern(SocketPermissionRequest::TCP_LISTEN); + } + + return scoped_ptr<base::Value>(sockets.ToValue().release()).Pass(); +} + +ManifestPermission* SocketsManifestPermission::Clone() const { + scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission()); + result->permissions_ = permissions_; + result->kinds_ = kinds_; + return result.release(); +} + +ManifestPermission* SocketsManifestPermission::Diff( + const ManifestPermission* rhs) const { + const SocketsManifestPermission* other = + static_cast<const SocketsManifestPermission*>(rhs); + + scoped_ptr<SocketsManifestPermission> data(new SocketsManifestPermission()); + std::set_difference( + permissions_.begin(), permissions_.end(), + other->permissions_.begin(), other->permissions_.end(), + std::inserter<SocketPermissionEntrySet>( + data->permissions_, data->permissions_.begin())); + + data->kinds_ = (kinds_ & (~other->kinds_)); + + // Note: We may need to fix up |kinds_| because any permission entry + // in a given group (udp, tcp, etc.) implies the corresponding kind bit set. + data->kinds_ |= HasOperationType(data->permissions_, + SocketPermissionRequest::UDP_BIND, kUdpPermission); + data->kinds_ |= HasOperationType(data->permissions_, + SocketPermissionRequest::UDP_SEND_TO, kUdpPermission); + data->kinds_ |= HasOperationType(data->permissions_, + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kUdpPermission); + data->kinds_ |= HasOperationType(data->permissions_, + SocketPermissionRequest::TCP_CONNECT, kTcpPermission); + data->kinds_ |= HasOperationType(data->permissions_, + SocketPermissionRequest::TCP_LISTEN, kTcpServerPermission); + return data.release(); +} + +ManifestPermission* SocketsManifestPermission::Union( + const ManifestPermission* rhs) const { + const SocketsManifestPermission* other = + static_cast<const SocketsManifestPermission*>(rhs); + + scoped_ptr<SocketsManifestPermission> data(new SocketsManifestPermission()); + std::set_union( + permissions_.begin(), permissions_.end(), + other->permissions_.begin(), other->permissions_.end(), + std::inserter<SocketPermissionEntrySet>( + data->permissions_, data->permissions_.begin())); + + data->kinds_ = (kinds_ | other->kinds_); + return data.release(); +} + +ManifestPermission* SocketsManifestPermission::Intersect( + const ManifestPermission* rhs) const { + const SocketsManifestPermission* other = + static_cast<const SocketsManifestPermission*>(rhs); + + scoped_ptr<SocketsManifestPermission> data(new SocketsManifestPermission()); + std::set_intersection( + permissions_.begin(), permissions_.end(), + other->permissions_.begin(), other->permissions_.end(), + std::inserter<SocketPermissionEntrySet>( + data->permissions_, data->permissions_.begin())); + + data->kinds_ = (kinds_ & other->kinds_); + return data.release(); +} + +bool SocketsManifestPermission::Contains(const ManifestPermission* rhs) const { + const SocketsManifestPermission* other = + static_cast<const SocketsManifestPermission*>(rhs); + + return std::includes( + permissions_.begin(), permissions_.end(), + other->permissions_.begin(), other->permissions_.end()) && + ((kinds_ | other->kinds_) == kinds_); +} + +bool SocketsManifestPermission::Equal(const ManifestPermission* rhs) const { + const SocketsManifestPermission* other = + static_cast<const SocketsManifestPermission*>(rhs); + + return (permissions_ == other->permissions_) && + (kinds_ == other->kinds_); +} + +void SocketsManifestPermission::Write(IPC::Message* m) const { + IPC::WriteParam(m, permissions_); + IPC::WriteParam(m, kinds_); +} + +bool SocketsManifestPermission::Read(const IPC::Message* m, + PickleIterator* iter) { + return IPC::ReadParam(m, iter, &permissions_) && + IPC::ReadParam(m, iter, &kinds_); +} + +void SocketsManifestPermission::Log(std::string* log) const { + IPC::LogParam(permissions_, log); + IPC::LogParam(kinds_, log); +} + +// static +bool SocketsManifestPermission::ParseHostPattern( + SocketsManifestPermission* manifest_data, + SocketPermissionRequest::OperationType operation_type, + const scoped_ptr<std::string>& value, + string16* error) { + if (value) { + SocketPermissionEntry entry; + if (!SocketPermissionEntry::ParseHostPattern( + operation_type, *value, &entry)) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kErrorInvalidHostPattern, *value); + return false; + } + manifest_data->AddPermission(entry); + } + return true; +} + +// static +SocketsManifestPermission::PermissionKind SocketsManifestPermission:: + HasOperationType(const SocketPermissionEntrySet& set, + SocketPermissionRequest::OperationType operation_type, + PermissionKind kind) { + for (SocketPermissionEntrySet::const_iterator it = set.begin(); + it != set.end() ; ++it) { + if (it->pattern().type == operation_type) + return kind; + } + return kNone; +} + + +scoped_ptr<std::string> SocketsManifestPermission::CreateHostPattern( + SocketPermissionRequest::OperationType operation_type) const { + scoped_ptr<std::string> result; + for (SocketPermissionEntrySet::const_iterator it = + entries().begin(); it != entries().end() ; ++it) { + if (it->pattern().type == operation_type) { + result.reset(new std::string(it->GetHostPatternAsString())); + break; + } + } + return result.Pass(); +} + +void SocketsManifestPermission::AddPermission( + const SocketPermissionEntry& entry) { + permissions_.insert(entry); +} + +bool SocketsManifestPermission::AddAnyHostMessage( + PermissionMessages& messages) const { + for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->IsAddressBoundType() && + it->GetHostType() == SocketPermissionEntry::ANY_HOST) { + messages.push_back(PermissionMessage( + PermissionMessage::kSocketAnyHost, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_SOCKET_ANY_HOST))); + return true; + } + } + return false; +} + +void SocketsManifestPermission::AddSubdomainHostMessage( + PermissionMessages& messages) const { + std::set<string16> domains; + for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS) + domains.insert(UTF8ToUTF16(it->pattern().host)); + } + if (!domains.empty()) { + int id = (domains.size() == 1) ? + IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN : + IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS; + messages.push_back(PermissionMessage( + PermissionMessage::kSocketDomainHosts, + l10n_util::GetStringFUTF16( + id, + JoinString( + std::vector<string16>( + domains.begin(), domains.end()), ' ')))); + } +} + +void SocketsManifestPermission::AddSpecificHostMessage( + PermissionMessages& messages) const { + std::set<string16> hostnames; + for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS) + hostnames.insert(UTF8ToUTF16(it->pattern().host)); + } + if (!hostnames.empty()) { + int id = (hostnames.size() == 1) ? + IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST : + IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS; + messages.push_back(PermissionMessage( + PermissionMessage::kSocketSpecificHosts, + l10n_util::GetStringFUTF16( + id, + JoinString( + std::vector<string16>( + hostnames.begin(), hostnames.end()), ' ')))); + } +} + +void SocketsManifestPermission::AddNetworkListMessage( + PermissionMessages& messages) const { + for (SocketPermissionEntrySet::const_iterator it = permissions_.begin(); + it != permissions_.end(); ++it) { + if (it->pattern().type == SocketPermissionRequest::NETWORK_STATE) { + messages.push_back(PermissionMessage( + PermissionMessage::kNetworkState, + l10n_util::GetStringUTF16( + IDS_EXTENSION_PROMPT_WARNING_NETWORK_STATE))); + } + } +} + +} // namespace extensions diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_permission.h b/chrome/common/extensions/api/sockets/sockets_manifest_permission.h new file mode 100644 index 0000000..ca65b0f --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_permission.h @@ -0,0 +1,100 @@ +// Copyright 2013 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. + +#ifndef CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_PERMISSION_H_ +#define CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_PERMISSION_H_ + +#include <set> +#include <vector> + +#include "chrome/common/extensions/permissions/socket_permission_entry.h" +#include "extensions/common/install_warning.h" +#include "extensions/common/permissions/manifest_permission.h" + +namespace content { +struct SocketPermissionRequest; +} + +namespace extensions { +class Extension; +} + +namespace extensions { + +class SocketsManifestPermission : public ManifestPermission { + public: + typedef std::set<SocketPermissionEntry> SocketPermissionEntrySet; + enum PermissionKind { + kNone = 0, + kTcpPermission = 1 << 0, + kUdpPermission = 1 << 1, + kTcpServerPermission = 1 << 2 + }; + + SocketsManifestPermission(); + virtual ~SocketsManifestPermission(); + + // Tries to construct the info based on |value|, as it would have appeared in + // the manifest. Sets |error| and returns an empty scoped_ptr on failure. + static scoped_ptr<SocketsManifestPermission> FromValue( + const base::Value& value, + string16* error); + + bool CheckRequest(const Extension* extension, + const content::SocketPermissionRequest& request) const; + + // extensions::ManifestPermission overrides. + virtual std::string name() const OVERRIDE; + virtual std::string id() const OVERRIDE; + virtual bool HasMessages() const OVERRIDE; + virtual PermissionMessages GetMessages() const OVERRIDE; + virtual bool FromValue(const base::Value* value) OVERRIDE; + virtual scoped_ptr<base::Value> ToValue() const OVERRIDE; + virtual ManifestPermission* Clone() const OVERRIDE; + virtual ManifestPermission* Diff(const ManifestPermission* rhs) const + OVERRIDE; + virtual ManifestPermission* Union(const ManifestPermission* rhs) const + OVERRIDE; + virtual ManifestPermission* Intersect(const ManifestPermission* rhs) const + OVERRIDE; + virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE; + virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE; + virtual void Write(IPC::Message* m) const OVERRIDE; + virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE; + virtual void Log(std::string* log) const OVERRIDE; + + bool has_udp() const { return (kinds_ & kUdpPermission) != 0; } + bool has_tcp() const { return (kinds_ & kTcpPermission) != 0; } + bool has_tcp_server() const { return (kinds_ & kTcpServerPermission) != 0; } + const SocketPermissionEntrySet& entries() const { return permissions_; } + + private: + static bool ParseHostPattern( + SocketsManifestPermission* manifest_data, + content::SocketPermissionRequest::OperationType operation_type, + const scoped_ptr<std::string>& value, + string16* error); + + static PermissionKind HasOperationType( + const SocketPermissionEntrySet& set, + content::SocketPermissionRequest::OperationType operation_type, + PermissionKind kind); + + scoped_ptr<std::string> CreateHostPattern( + content::SocketPermissionRequest::OperationType operation_type) const; + + void AddPermission(const SocketPermissionEntry& entry); + + bool AddAnyHostMessage(PermissionMessages& messages) const; + void AddSubdomainHostMessage(PermissionMessages& messages) const; + void AddSpecificHostMessage(PermissionMessages& messages) const; + void AddNetworkListMessage(PermissionMessages& messages) const; + + SocketPermissionEntrySet permissions_; + int kinds_; // PermissionKind bits +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_API_SOCKETS_SOCKETS_MANIFEST_PERMISSION_H_ diff --git a/chrome/common/extensions/api/sockets/sockets_manifest_permission_unittest.cc b/chrome/common/extensions/api/sockets/sockets_manifest_permission_unittest.cc new file mode 100644 index 0000000..67ece853 --- /dev/null +++ b/chrome/common/extensions/api/sockets/sockets_manifest_permission_unittest.cc @@ -0,0 +1,186 @@ +// Copyright 2013 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 "base/json/json_reader.h" +#include "base/pickle.h" +#include "base/values.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_permission.h" +#include "chrome/common/extensions/extension_messages.h" +#include "extensions/common/manifest_constants.h" +#include "ipc/ipc_message.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { + +static void AssertEmptyPermission(const SocketsManifestPermission* permission) { + EXPECT_TRUE(permission); + EXPECT_EQ(std::string(extensions::manifest_keys::kSockets), permission->id()); + EXPECT_EQ(permission->id(), permission->name()); + EXPECT_FALSE(permission->HasMessages()); + EXPECT_EQ(0u, permission->entries().size()); +} + +} // namespace + +TEST(SocketsManifestPermissionTest, Empty) { + // Construction + scoped_ptr<SocketsManifestPermission> permission( + new SocketsManifestPermission()); + AssertEmptyPermission(permission.get()); + + // Clone()/Equal() + scoped_ptr<SocketsManifestPermission> clone( + static_cast<SocketsManifestPermission*>(permission->Clone())); + AssertEmptyPermission(clone.get()); + + EXPECT_TRUE(permission->Equal(clone.get())); + + // ToValue()/FromValue() + scoped_ptr<const base::Value> value(permission->ToValue()); + EXPECT_TRUE(value.get()); + + scoped_ptr<SocketsManifestPermission> permission2( + new SocketsManifestPermission()); + EXPECT_TRUE(permission2->FromValue(value.get())); + AssertEmptyPermission(permission2.get()); + + // Union/Diff/Intersection + scoped_ptr<SocketsManifestPermission> diff_perm( + static_cast<SocketsManifestPermission*>(permission->Diff(clone.get()))); + AssertEmptyPermission(diff_perm.get()); + + scoped_ptr<SocketsManifestPermission> union_perm( + static_cast<SocketsManifestPermission*>(permission->Union(clone.get()))); + AssertEmptyPermission(union_perm.get()); + + scoped_ptr<SocketsManifestPermission> intersect_perm( + static_cast<SocketsManifestPermission*>( + permission->Intersect(clone.get()))); + AssertEmptyPermission(intersect_perm.get()); + + // IPC + scoped_ptr<SocketsManifestPermission> ipc_perm( + new SocketsManifestPermission()); + scoped_ptr<SocketsManifestPermission> ipc_perm2( + new SocketsManifestPermission()); + + IPC::Message m; + ipc_perm->Write(&m); + PickleIterator iter(m); + EXPECT_TRUE(ipc_perm2->Read(&m, &iter)); + AssertEmptyPermission(ipc_perm2.get()); +} + +TEST(SocketsManifestPermissionTest, General) { + std::string udp_send_string = "{ \"udp\": { \"send\": \"\" } }"; + scoped_ptr<base::Value> udp_send(base::JSONReader::Read(udp_send_string)); + EXPECT_TRUE(udp_send); + + std::string udp_bind_string = "{ \"udp\": { \"bind\": \"127.0.0.1:3007\" } }"; + scoped_ptr<base::Value> udp_bind(base::JSONReader::Read(udp_bind_string)); + EXPECT_TRUE(udp_bind); + + std::string tcp_connect_string = + "{ \"tcp\": { \"connect\": \"127.0.0.1:80\" } }"; + scoped_ptr<base::Value> tcp_connect( + base::JSONReader::Read(tcp_connect_string)); + EXPECT_TRUE(tcp_connect); + + std::string tcp_server_listen_string = + "{ \"tcpServer\": { \"listen\": \"127.0.0.1:80\" } }"; + scoped_ptr<base::Value> tcp_server_listen( + base::JSONReader::Read(tcp_server_listen_string)); + EXPECT_TRUE(tcp_server_listen); + + // FromValue() + scoped_ptr<SocketsManifestPermission> permission1( + new SocketsManifestPermission()); + EXPECT_TRUE(permission1->FromValue(udp_send.get())); + EXPECT_EQ(1u, permission1->entries().size()); + + scoped_ptr<SocketsManifestPermission> permission2( + new SocketsManifestPermission()); + EXPECT_TRUE(permission2->FromValue(udp_bind.get())); + EXPECT_EQ(1u, permission2->entries().size()); + + scoped_ptr<SocketsManifestPermission> permission3( + new SocketsManifestPermission()); + EXPECT_TRUE(permission3->FromValue(tcp_connect.get())); + EXPECT_EQ(1u, permission3->entries().size()); + + scoped_ptr<SocketsManifestPermission> permission4( + new SocketsManifestPermission()); + EXPECT_TRUE(permission4->FromValue(tcp_server_listen.get())); + EXPECT_EQ(1u, permission4->entries().size()); + + // ToValue() + scoped_ptr<base::Value> value1 = permission1->ToValue(); + EXPECT_TRUE(value1); + scoped_ptr<SocketsManifestPermission> permission1_1( + new SocketsManifestPermission()); + EXPECT_TRUE(permission1_1->FromValue(value1.get())); + EXPECT_TRUE(permission1->Equal(permission1_1.get())); + + scoped_ptr<base::Value> value2 = permission2->ToValue(); + EXPECT_TRUE(value2); + scoped_ptr<SocketsManifestPermission> permission2_1( + new SocketsManifestPermission()); + EXPECT_TRUE(permission2_1->FromValue(value2.get())); + EXPECT_TRUE(permission2->Equal(permission2_1.get())); + + scoped_ptr<base::Value> value3 = permission3->ToValue(); + EXPECT_TRUE(value3); + scoped_ptr<SocketsManifestPermission> permission3_1( + new SocketsManifestPermission()); + EXPECT_TRUE(permission3_1->FromValue(value3.get())); + EXPECT_TRUE(permission3->Equal(permission3_1.get())); + + scoped_ptr<base::Value> value4 = permission4->ToValue(); + EXPECT_TRUE(value4); + scoped_ptr<SocketsManifestPermission> permission4_1( + new SocketsManifestPermission()); + EXPECT_TRUE(permission4_1->FromValue(value4.get())); + EXPECT_TRUE(permission4->Equal(permission4_1.get())); + + // Union/Diff/Intersection + scoped_ptr<SocketsManifestPermission> union_perm( + static_cast<SocketsManifestPermission*>( + permission1->Union(permission2.get()))); + EXPECT_TRUE(union_perm); + EXPECT_EQ(2u, union_perm->entries().size()); + + EXPECT_TRUE(union_perm->Contains(permission1.get())); + EXPECT_TRUE(union_perm->Contains(permission2.get())); + EXPECT_FALSE(union_perm->Contains(permission3.get())); + EXPECT_FALSE(union_perm->Contains(permission4.get())); + + scoped_ptr<SocketsManifestPermission> diff_perm1( + static_cast<SocketsManifestPermission*>( + permission1->Diff(permission2.get()))); + EXPECT_TRUE(diff_perm1); + EXPECT_EQ(1u, diff_perm1->entries().size()); + + EXPECT_TRUE(permission1->Equal(diff_perm1.get())); + EXPECT_TRUE(diff_perm1->Equal(permission1.get())); + + scoped_ptr<SocketsManifestPermission> diff_perm2( + static_cast<SocketsManifestPermission*>( + permission1->Diff(union_perm.get()))); + EXPECT_TRUE(diff_perm2); + AssertEmptyPermission(diff_perm2.get()); + + scoped_ptr<SocketsManifestPermission> intersect_perm1( + static_cast<SocketsManifestPermission*>( + union_perm->Intersect(permission1.get()))); + EXPECT_TRUE(intersect_perm1); + EXPECT_EQ(1u, intersect_perm1->entries().size()); + + EXPECT_TRUE(permission1->Equal(intersect_perm1.get())); + EXPECT_TRUE(intersect_perm1->Equal(permission1.get())); + +} + +} // namespace extensions diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc index 3363b79..e832639 100644 --- a/chrome/common/extensions/chrome_manifest_handlers.cc +++ b/chrome/common/extensions/chrome_manifest_handlers.cc @@ -20,7 +20,7 @@ #include "chrome/common/extensions/api/media_galleries_private/media_galleries_handler.h" #include "chrome/common/extensions/api/omnibox/omnibox_handler.h" #include "chrome/common/extensions/api/plugins/plugins_handler.h" -#include "chrome/common/extensions/api/sockets/sockets_handler.h" +#include "chrome/common/extensions/api/sockets/sockets_manifest_handler.h" #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h" #include "chrome/common/extensions/api/spellcheck/spellcheck_handler.h" #include "chrome/common/extensions/api/system_indicator/system_indicator_handler.h" @@ -90,7 +90,7 @@ void RegisterChromeManifestHandlers() { (new SettingsOverridesHandler)->Register(); (new ScriptBadgeHandler)->Register(); (new SharedModuleHandler)->Register(); - (new SocketsHandler)->Register(); + (new SocketsManifestHandler)->Register(); (new SpellcheckHandler)->Register(); (new StorageSchemaManifestHandler)->Register(); (new SystemIndicatorHandler)->Register(); diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index e118bdc..71018cc 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -557,6 +557,7 @@ bool Extension::InitFromValue(int flags, string16* error) { finished_parsing_manifest_ = true; + permissions_data_->InitializeManifestPermissions(this); permissions_data_->FinalizePermissions(this); return true; diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index d88b4da..2bb79eb 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -45,6 +45,7 @@ class ImageSkia; namespace extensions { class PermissionsData; class APIPermissionSet; +class ManifestPermissionSet; class PermissionSet; // Represents a Chrome extension. diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc index 0e3c5e4..a8acbf8 100644 --- a/chrome/common/extensions/extension_messages.cc +++ b/chrome/common/extensions/extension_messages.cc @@ -9,14 +9,17 @@ #include "chrome/common/extensions/permissions/permissions_data.h" #include "content/public/common/common_param_traits.h" #include "extensions/common/manifest.h" +#include "extensions/common/manifest_handler.h" #include "extensions/common/permissions/permissions_info.h" using extensions::APIPermission; using extensions::APIPermissionInfo; -using extensions::APIPermissionMap; using extensions::APIPermissionSet; using extensions::Extension; using extensions::Manifest; +using extensions::ManifestHandler; +using extensions::ManifestPermission; +using extensions::ManifestPermissionSet; using extensions::PermissionSet; using extensions::URLPatternSet; @@ -32,6 +35,8 @@ ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params( location(extension->location()), path(extension->path()), apis(extension->GetActivePermissions()->apis()), + manifest_permissions( + extension->GetActivePermissions()->manifest_permissions()), explicit_hosts(extension->GetActivePermissions()->explicit_hosts()), scriptable_hosts(extension->GetActivePermissions()->scriptable_hosts()), id(extension->id()), @@ -45,7 +50,8 @@ scoped_refptr<Extension> ExtensionMsg_Loaded_Params::ConvertToExtension( if (extension.get()) { extensions::PermissionsData::SetActivePermissions( extension.get(), - new PermissionSet(apis, explicit_hosts, scriptable_hosts)); + new PermissionSet(apis, manifest_permissions, + explicit_hosts, scriptable_hosts)); } return extension; } @@ -183,6 +189,41 @@ void ParamTraits<APIPermissionSet>::Log( LogParam(p.map(), l); } +void ParamTraits<ManifestPermissionSet>::Write( + Message* m, const param_type& p) { + ManifestPermissionSet::const_iterator it = p.begin(); + const ManifestPermissionSet::const_iterator end = p.end(); + WriteParam(m, p.size()); + for (; it != end; ++it) { + WriteParam(m, it->name()); + it->Write(m); + } +} + +bool ParamTraits<ManifestPermissionSet>::Read( + const Message* m, PickleIterator* iter, param_type* r) { + size_t size; + if (!ReadParam(m, iter, &size)) + return false; + for (size_t i = 0; i < size; ++i) { + std::string name; + if (!ReadParam(m, iter, &name)) + return false; + scoped_ptr<ManifestPermission> p(ManifestHandler::CreatePermission(name)); + if (!p) + return false; + if (!p->Read(m, iter)) + return false; + r->insert(p.release()); + } + return true; +} + +void ParamTraits<ManifestPermissionSet>::Log( + const param_type& p, std::string* l) { + LogParam(p.map(), l); +} + void ParamTraits<ExtensionMsg_Loaded_Params>::Write(Message* m, const param_type& p) { WriteParam(m, p.location); diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 8e0d843..f7f6a09 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -143,6 +143,16 @@ IPC_STRUCT_BEGIN(ExtensionMsg_ExternalConnectionInfo) IPC_STRUCT_MEMBER(GURL, source_url) IPC_STRUCT_END() +// Parameters structure for ExtensionMsg_UpdatePermissions. +IPC_STRUCT_BEGIN(ExtensionMsg_UpdatePermissions_Params) + IPC_STRUCT_MEMBER(int /* UpdateExtensionPermissionsInfo::REASON */, reason_id) + IPC_STRUCT_MEMBER(std::string, extension_id) + IPC_STRUCT_MEMBER(extensions::APIPermissionSet, apis) + IPC_STRUCT_MEMBER(extensions::ManifestPermissionSet, manifest_permissions) + IPC_STRUCT_MEMBER(extensions::URLPatternSet, explicit_hosts) + IPC_STRUCT_MEMBER(extensions::URLPatternSet, scriptable_hosts) +IPC_STRUCT_END() + IPC_STRUCT_TRAITS_BEGIN(WebApplicationInfo::IconInfo) IPC_STRUCT_TRAITS_MEMBER(url) IPC_STRUCT_TRAITS_MEMBER(width) @@ -233,6 +243,7 @@ struct ExtensionMsg_Loaded_Params { // The extension's active permissions. extensions::APIPermissionSet apis; + extensions::ManifestPermissionSet manifest_permissions; extensions::URLPatternSet explicit_hosts; extensions::URLPatternSet scriptable_hosts; @@ -284,6 +295,14 @@ struct ParamTraits<extensions::APIPermissionSet> { }; template <> +struct ParamTraits<extensions::ManifestPermissionSet> { + typedef extensions::ManifestPermissionSet param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> struct ParamTraits<ExtensionMsg_Loaded_Params> { typedef ExtensionMsg_Loaded_Params param_type; static void Write(Message* m, const param_type& p); @@ -374,12 +393,8 @@ IPC_MESSAGE_ROUTED1(ExtensionMsg_SetTabId, int /* id of tab */) // Tell the renderer to update an extension's permission set. -IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions, - int /* UpdateExtensionPermissionsInfo::REASON */, - std::string /* extension_id */, - extensions::APIPermissionSet /* permissions */, - extensions::URLPatternSet /* explicit_hosts */, - extensions::URLPatternSet /* scriptable_hosts */) +IPC_MESSAGE_CONTROL1(ExtensionMsg_UpdatePermissions, + ExtensionMsg_UpdatePermissions_Params) // Tell the renderer about new tab-specific permissions for an extension. IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdateTabSpecificPermissions, diff --git a/chrome/common/extensions/permissions/chrome_permission_message_provider.cc b/chrome/common/extensions/permissions/chrome_permission_message_provider.cc index e5105cb..cd04710 100644 --- a/chrome/common/extensions/permissions/chrome_permission_message_provider.cc +++ b/chrome/common/extensions/permissions/chrome_permission_message_provider.cc @@ -37,8 +37,12 @@ PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages( std::set<PermissionMessage> host_msgs = GetHostPermissionMessages(permissions, extension_type); std::set<PermissionMessage> api_msgs = GetAPIPermissionMessages(permissions); + std::set<PermissionMessage> manifest_permission_msgs = + GetManifestPermissionMessages(permissions); messages.insert(messages.end(), host_msgs.begin(), host_msgs.end()); messages.insert(messages.end(), api_msgs.begin(), api_msgs.end()); + messages.insert(messages.end(), manifest_permission_msgs.begin(), + manifest_permission_msgs.end()); return messages; } @@ -193,6 +197,22 @@ ChromePermissionMessageProvider::GetAPIPermissionMessages( } std::set<PermissionMessage> +ChromePermissionMessageProvider::GetManifestPermissionMessages( + const PermissionSet* permissions) const { + std::set<PermissionMessage> messages; + for (ManifestPermissionSet::const_iterator permission_it = + permissions->manifest_permissions().begin(); + permission_it != permissions->manifest_permissions().end(); + ++permission_it) { + if (permission_it->HasMessages()) { + PermissionMessages new_messages = permission_it->GetMessages(); + messages.insert(new_messages.begin(), new_messages.end()); + } + } + return messages; +} + +std::set<PermissionMessage> ChromePermissionMessageProvider::GetHostPermissionMessages( const PermissionSet* permissions, Manifest::Type extension_type) const { diff --git a/chrome/common/extensions/permissions/chrome_permission_message_provider.h b/chrome/common/extensions/permissions/chrome_permission_message_provider.h index 05125f4..44b122c 100644 --- a/chrome/common/extensions/permissions/chrome_permission_message_provider.h +++ b/chrome/common/extensions/permissions/chrome_permission_message_provider.h @@ -39,6 +39,10 @@ class ChromePermissionMessageProvider : public PermissionMessageProvider { std::set<PermissionMessage> GetAPIPermissionMessages( const PermissionSet* permissions) const; + // Gets the permission messages for the Manifest permissions. + std::set<PermissionMessage> GetManifestPermissionMessages( + const PermissionSet* permissions) const; + // Gets the permission messages for the host permissions. std::set<PermissionMessage> GetHostPermissionMessages( const PermissionSet* permissions, diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 7ee32eb..302df86 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -202,6 +202,7 @@ TEST(PermissionsTest, EffectiveHostPermissions) { TEST(PermissionsTest, ExplicitAccessToOrigin) { APIPermissionSet apis; + ManifestPermissionSet manifest_permissions; URLPatternSet explicit_hosts; URLPatternSet scriptable_hosts; @@ -210,7 +211,7 @@ TEST(PermissionsTest, ExplicitAccessToOrigin) { AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*"); scoped_refptr<PermissionSet> perm_set = new PermissionSet( - apis, explicit_hosts, scriptable_hosts); + apis, manifest_permissions, explicit_hosts, scriptable_hosts); ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin( GURL("http://www.google.com/"))); ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin( @@ -226,6 +227,7 @@ TEST(PermissionsTest, ExplicitAccessToOrigin) { TEST(PermissionsTest, CreateUnion) { APIPermission* permission = NULL; + ManifestPermissionSet manifest_permissions; APIPermissionSet apis1; APIPermissionSet apis2; APIPermissionSet expected_apis; @@ -270,8 +272,10 @@ TEST(PermissionsTest, CreateUnion) { AddPattern(&expected_explicit_hosts, "http://*.google.com/*"); AddPattern(&effective_hosts, "http://*.google.com/*"); - set1 = new PermissionSet(apis1, explicit_hosts1, scriptable_hosts1); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set1 = new PermissionSet(apis1, manifest_permissions, + explicit_hosts1, scriptable_hosts1); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); union_set = PermissionSet::CreateUnion(set1.get(), set2.get()); EXPECT_TRUE(set1->Contains(*set2.get())); EXPECT_TRUE(set1->Contains(*union_set.get())); @@ -332,7 +336,8 @@ TEST(PermissionsTest, CreateUnion) { URLPatternSet::CreateUnion( explicit_hosts2, scriptable_hosts2, &effective_hosts); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); union_set = PermissionSet::CreateUnion(set1.get(), set2.get()); EXPECT_FALSE(set1->Contains(*set2.get())); @@ -353,6 +358,7 @@ TEST(PermissionsTest, CreateUnion) { TEST(PermissionsTest, CreateIntersection) { APIPermission* permission = NULL; + ManifestPermissionSet manifest_permissions; APIPermissionSet apis1; APIPermissionSet apis2; APIPermissionSet expected_apis; @@ -393,8 +399,10 @@ TEST(PermissionsTest, CreateIntersection) { AddPattern(&explicit_hosts1, "http://*.google.com/*"); AddPattern(&scriptable_hosts1, "http://www.reddit.com/*"); - set1 = new PermissionSet(apis1, explicit_hosts1, scriptable_hosts1); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set1 = new PermissionSet(apis1, manifest_permissions, + explicit_hosts1, scriptable_hosts1); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); new_set = PermissionSet::CreateIntersection(set1.get(), set2.get()); EXPECT_TRUE(set1->Contains(*new_set.get())); EXPECT_TRUE(set2->Contains(*new_set.get())); @@ -447,7 +455,8 @@ TEST(PermissionsTest, CreateIntersection) { effective_hosts.ClearPatterns(); AddPattern(&effective_hosts, "http://*.google.com/*"); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); new_set = PermissionSet::CreateIntersection(set1.get(), set2.get()); EXPECT_TRUE(set1->Contains(*new_set.get())); @@ -468,6 +477,7 @@ TEST(PermissionsTest, CreateIntersection) { TEST(PermissionsTest, CreateDifference) { APIPermission* permission = NULL; + ManifestPermissionSet manifest_permissions; APIPermissionSet apis1; APIPermissionSet apis2; APIPermissionSet expected_apis; @@ -508,8 +518,10 @@ TEST(PermissionsTest, CreateDifference) { AddPattern(&explicit_hosts1, "http://*.google.com/*"); AddPattern(&scriptable_hosts1, "http://www.reddit.com/*"); - set1 = new PermissionSet(apis1, explicit_hosts1, scriptable_hosts1); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set1 = new PermissionSet(apis1, manifest_permissions, + explicit_hosts1, scriptable_hosts1); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); new_set = PermissionSet::CreateDifference(set1.get(), set2.get()); EXPECT_EQ(*set1.get(), *new_set.get()); @@ -550,7 +562,8 @@ TEST(PermissionsTest, CreateDifference) { effective_hosts.ClearPatterns(); AddPattern(&effective_hosts, "http://www.reddit.com/*"); - set2 = new PermissionSet(apis2, explicit_hosts2, scriptable_hosts2); + set2 = new PermissionSet(apis2, manifest_permissions, + explicit_hosts2, scriptable_hosts2); new_set = PermissionSet::CreateDifference(set1.get(), set2.get()); EXPECT_TRUE(set1->Contains(*new_set.get())); @@ -773,7 +786,8 @@ TEST(PermissionsTest, FileSystemPermissionMessages) { api_permissions.insert(APIPermission::kFileSystemWrite); api_permissions.insert(APIPermission::kFileSystemDirectory); scoped_refptr<PermissionSet> permissions( - new PermissionSet(api_permissions, URLPatternSet(), URLPatternSet())); + new PermissionSet(api_permissions, ManifestPermissionSet(), + URLPatternSet(), URLPatternSet())); PermissionMessages messages = PermissionMessageProvider::Get()->GetPermissionMessages( permissions, Manifest::TYPE_PLATFORM_APP); @@ -794,7 +808,8 @@ TEST(PermissionsTest, HiddenFileSystemPermissionMessages) { api_permissions.insert(APIPermission::kFileSystemDirectory); api_permissions.insert(APIPermission::kFileSystemWriteDirectory); scoped_refptr<PermissionSet> permissions( - new PermissionSet(api_permissions, URLPatternSet(), URLPatternSet())); + new PermissionSet(api_permissions, ManifestPermissionSet(), + URLPatternSet(), URLPatternSet())); PermissionMessages messages = PermissionMessageProvider::Get()->GetPermissionMessages( permissions, Manifest::TYPE_PLATFORM_APP); @@ -805,19 +820,24 @@ TEST(PermissionsTest, HiddenFileSystemPermissionMessages) { TEST(PermissionsTest, MergedFileSystemPermissionComparison) { APIPermissionSet write_api_permissions; write_api_permissions.insert(APIPermission::kFileSystemWrite); - scoped_refptr<PermissionSet> write_permissions(new PermissionSet( - write_api_permissions, URLPatternSet(), URLPatternSet())); + scoped_refptr<PermissionSet> write_permissions( + new PermissionSet(write_api_permissions, ManifestPermissionSet(), + URLPatternSet(), URLPatternSet())); APIPermissionSet directory_api_permissions; directory_api_permissions.insert(APIPermission::kFileSystemDirectory); - scoped_refptr<PermissionSet> directory_permissions(new PermissionSet( - directory_api_permissions, URLPatternSet(), URLPatternSet())); + scoped_refptr<PermissionSet> directory_permissions( + new PermissionSet(directory_api_permissions, ManifestPermissionSet(), + URLPatternSet(), URLPatternSet())); APIPermissionSet write_directory_api_permissions; write_directory_api_permissions.insert( APIPermission::kFileSystemWriteDirectory); - scoped_refptr<PermissionSet> write_directory_permissions(new PermissionSet( - write_directory_api_permissions, URLPatternSet(), URLPatternSet())); + scoped_refptr<PermissionSet> write_directory_permissions( + new PermissionSet(write_directory_api_permissions, + ManifestPermissionSet(), + URLPatternSet(), + URLPatternSet())); const PermissionMessageProvider* provider = PermissionMessageProvider::Get(); EXPECT_FALSE(provider->IsPrivilegeIncrease(write_directory_permissions, @@ -1161,7 +1181,8 @@ TEST(PermissionsTest, GetDistinctHosts) { expected.insert("*.example.com"); scoped_refptr<PermissionSet> perm_set(new PermissionSet( - empty_perms, explicit_hosts, scriptable_hosts)); + empty_perms, ManifestPermissionSet(), + explicit_hosts, scriptable_hosts)); EXPECT_EQ(expected, permission_message_util::GetDistinctHosts( perm_set->effective_hosts(), true, true)); @@ -1268,6 +1289,7 @@ TEST(PermissionsTest, GetDistinctHosts_FirstInListIs4thBestRcd) { TEST(PermissionsTest, IsHostPrivilegeIncrease) { Manifest::Type type = Manifest::TYPE_EXTENSION; const PermissionMessageProvider* provider = PermissionMessageProvider::Get(); + ManifestPermissionSet empty_manifest_permissions; URLPatternSet elist1; URLPatternSet elist2; URLPatternSet slist1; @@ -1286,8 +1308,10 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); - set1 = new PermissionSet(empty_perms, elist1, slist1); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set1 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist1, slist1); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_FALSE(provider->IsPrivilegeIncrease(set1, set2, type)); EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1296,7 +1320,8 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*")); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_FALSE(provider->IsPrivilegeIncrease(set1, set2, type)); EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1304,7 +1329,8 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*")); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_FALSE(provider->IsPrivilegeIncrease(set1, set2, type)); EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1312,7 +1338,8 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*")); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_TRUE(provider->IsPrivilegeIncrease(set1, set2, type)); // TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337 // EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1323,7 +1350,8 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path")); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_TRUE(provider->IsPrivilegeIncrease(set1, set2, type)); EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1331,7 +1359,8 @@ TEST(PermissionsTest, IsHostPrivilegeIncrease) { elist2.ClearPatterns(); elist2.AddPattern( URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*")); - set2 = new PermissionSet(empty_perms, elist2, slist2); + set2 = new PermissionSet(empty_perms, empty_manifest_permissions, + elist2, slist2); EXPECT_TRUE(provider->IsPrivilegeIncrease(set1, set2, type)); EXPECT_TRUE(provider->IsPrivilegeIncrease(set2, set1, type)); @@ -1351,7 +1380,7 @@ TEST(PermissionsTest, GetAPIsAsStrings) { apis.insert(APIPermission::kTab); scoped_refptr<PermissionSet> perm_set = new PermissionSet( - apis, empty_set, empty_set); + apis, ManifestPermissionSet(), empty_set, empty_set); std::set<std::string> api_names = perm_set->GetAPIsAsStrings(); // The result is correct if it has the same number of elements @@ -1369,25 +1398,26 @@ TEST(PermissionsTest, IsEmpty) { EXPECT_TRUE(empty->IsEmpty()); scoped_refptr<PermissionSet> perm_set; - perm_set = new PermissionSet(empty_apis, empty_extent, empty_extent); + perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(), + empty_extent, empty_extent); EXPECT_TRUE(perm_set->IsEmpty()); APIPermissionSet non_empty_apis; non_empty_apis.insert(APIPermission::kBackground); - perm_set = new PermissionSet( - non_empty_apis, empty_extent, empty_extent); + perm_set = new PermissionSet(non_empty_apis, ManifestPermissionSet(), + empty_extent, empty_extent); EXPECT_FALSE(perm_set->IsEmpty()); // Try non standard host URLPatternSet non_empty_extent; AddPattern(&non_empty_extent, "http://www.google.com/*"); - perm_set = new PermissionSet( - empty_apis, non_empty_extent, empty_extent); + perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(), + non_empty_extent, empty_extent); EXPECT_FALSE(perm_set->IsEmpty()); - perm_set = new PermissionSet( - empty_apis, empty_extent, non_empty_extent); + perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(), + empty_extent, non_empty_extent); EXPECT_FALSE(perm_set->IsEmpty()); } @@ -1399,7 +1429,8 @@ TEST(PermissionsTest, ImpliedPermissions) { EXPECT_EQ(2U, apis.size()); scoped_refptr<PermissionSet> perm_set; - perm_set = new PermissionSet(apis, empty_extent, empty_extent); + perm_set = new PermissionSet(apis, ManifestPermissionSet(), + empty_extent, empty_extent); EXPECT_EQ(4U, perm_set->apis().size()); } @@ -1429,7 +1460,8 @@ TEST(PermissionsTest, ChromeURLs) { allowed_hosts.AddPattern( URLPattern(URLPattern::SCHEME_ALL, "chrome://thumb/")); scoped_refptr<PermissionSet> permissions( - new PermissionSet(APIPermissionSet(), allowed_hosts, URLPatternSet())); + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + allowed_hosts, URLPatternSet())); PermissionMessageProvider::Get()-> GetPermissionMessages(permissions, Manifest::TYPE_EXTENSION); } diff --git a/chrome/common/extensions/permissions/permissions_data.cc b/chrome/common/extensions/permissions/permissions_data.cc index 961d719..e023712 100644 --- a/chrome/common/extensions/permissions/permissions_data.cc +++ b/chrome/common/extensions/permissions/permissions_data.cc @@ -12,6 +12,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" #include "extensions/common/error_utils.h" @@ -20,6 +21,7 @@ #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_message_provider.h" #include "extensions/common/permissions/permission_set.h" @@ -244,6 +246,7 @@ bool IsTrustedId(const std::string& extension_id) { struct PermissionsData::InitialPermissions { APIPermissionSet api_permissions; + ManifestPermissionSet manifest_permissions; URLPatternSet host_permissions; URLPatternSet scriptable_hosts; }; @@ -593,19 +596,27 @@ bool PermissionsData::ParsePermissions(Extension* extension, string16* error) { return true; } +void PermissionsData::InitializeManifestPermissions(Extension* extension) { + ManifestHandler::AddExtensionInitialRequiredPermissions( + extension, &initial_required_permissions_->manifest_permissions); +} + void PermissionsData::FinalizePermissions(Extension* extension) { active_permissions_ = new PermissionSet( initial_required_permissions_->api_permissions, + initial_required_permissions_->manifest_permissions, initial_required_permissions_->host_permissions, initial_required_permissions_->scriptable_hosts); required_permission_set_ = new PermissionSet( initial_required_permissions_->api_permissions, + initial_required_permissions_->manifest_permissions, initial_required_permissions_->host_permissions, initial_required_permissions_->scriptable_hosts); optional_permission_set_ = new PermissionSet( initial_optional_permissions_->api_permissions, + initial_optional_permissions_->manifest_permissions, initial_optional_permissions_->host_permissions, URLPatternSet()); diff --git a/chrome/common/extensions/permissions/permissions_data.h b/chrome/common/extensions/permissions/permissions_data.h index 60534ec..d0b6413 100644 --- a/chrome/common/extensions/permissions/permissions_data.h +++ b/chrome/common/extensions/permissions/permissions_data.h @@ -22,6 +22,7 @@ namespace extensions { class PermissionSet; class APIPermissionSet; class Extension; +class ManifestPermissionSet; class URLPatternSet; class UserScript; @@ -180,6 +181,9 @@ class PermissionsData { // Parse the permissions of a given extension in the initialization process. bool ParsePermissions(Extension* extension, string16* error); + // Ensure manifest handlers provide their custom manifest permissions. + void InitializeManifestPermissions(Extension* extension); + // Finalize permissions after the initialization process completes. void FinalizePermissions(Extension* extension); diff --git a/chrome/common/extensions/permissions/permissions_data_unittest.cc b/chrome/common/extensions/permissions/permissions_data_unittest.cc index c2bdb07..f4e0d3e 100644 --- a/chrome/common/extensions/permissions/permissions_data_unittest.cc +++ b/chrome/common/extensions/permissions/permissions_data_unittest.cc @@ -548,7 +548,8 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { { scoped_refptr<PermissionSet> permissions( - new PermissionSet(APIPermissionSet(), allowed_hosts, URLPatternSet())); + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + allowed_hosts, URLPatternSet())); PermissionsData::UpdateTabSpecificPermissions( extension.get(), 0, permissions); EXPECT_EQ(permissions->explicit_hosts(), @@ -576,7 +577,8 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { { scoped_refptr<PermissionSet> permissions( - new PermissionSet(APIPermissionSet(), allowed_hosts, URLPatternSet())); + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + allowed_hosts, URLPatternSet())); PermissionsData::UpdateTabSpecificPermissions( extension.get(), 0, permissions); EXPECT_EQ(permissions->explicit_hosts(), @@ -584,6 +586,7 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { ->explicit_hosts()); permissions = new PermissionSet(APIPermissionSet(), + ManifestPermissionSet(), more_allowed_hosts, URLPatternSet()); PermissionsData::UpdateTabSpecificPermissions( diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc index ea5ca21..943e97d 100644 --- a/chrome/renderer/extensions/dispatcher.cc +++ b/chrome/renderer/extensions/dispatcher.cc @@ -1375,17 +1375,23 @@ void Dispatcher::AddOrRemoveBindings(const std::string& extension_id) { base::Unretained(this))); } -void Dispatcher::OnUpdatePermissions(int reason_id, - const std::string& extension_id, - const APIPermissionSet& apis, - const URLPatternSet& explicit_hosts, - const URLPatternSet& scriptable_hosts) { +void Dispatcher::OnUpdatePermissions( + const ExtensionMsg_UpdatePermissions_Params& params) { + int reason_id = params.reason_id; + const std::string& extension_id = params.extension_id; + const APIPermissionSet& apis = params.apis; + const ManifestPermissionSet& manifest_permissions = + params.manifest_permissions; + const URLPatternSet& explicit_hosts = params.explicit_hosts; + const URLPatternSet& scriptable_hosts = params.scriptable_hosts; + const Extension* extension = extensions_.GetByID(extension_id); if (!extension) return; scoped_refptr<const PermissionSet> delta = - new PermissionSet(apis, explicit_hosts, scriptable_hosts); + new PermissionSet(apis, manifest_permissions, + explicit_hosts, scriptable_hosts); scoped_refptr<const PermissionSet> old_active = extension->GetActivePermissions(); UpdatedExtensionPermissionsInfo::Reason reason = @@ -1428,7 +1434,8 @@ void Dispatcher::OnUpdateTabSpecificPermissions( PermissionsData::UpdateTabSpecificPermissions( extension, tab_id, - new PermissionSet(APIPermissionSet(), origin_set, URLPatternSet())); + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + origin_set, URLPatternSet())); } void Dispatcher::OnClearTabSpecificPermissions( diff --git a/chrome/renderer/extensions/dispatcher.h b/chrome/renderer/extensions/dispatcher.h index 1501614..209f0e5 100644 --- a/chrome/renderer/extensions/dispatcher.h +++ b/chrome/renderer/extensions/dispatcher.h @@ -31,6 +31,7 @@ class ModuleSystem; class URLPattern; struct ExtensionMsg_ExternalConnectionInfo; struct ExtensionMsg_Loaded_Params; +struct ExtensionMsg_UpdatePermissions_Params; namespace blink { class WebFrame; @@ -50,6 +51,7 @@ namespace extensions { class ContentWatcher; class Extension; class FilteredEventRouter; +class ManifestPermissionSet; class RequestSender; class UserScriptSlave; struct Message; @@ -179,11 +181,7 @@ class Dispatcher : public content::RenderProcessObserver { void OnPageActionsUpdated(const std::string& extension_id, const std::vector<std::string>& page_actions); void OnActivateExtension(const std::string& extension_id); - void OnUpdatePermissions(int reason_id, - const std::string& extension_id, - const APIPermissionSet& apis, - const URLPatternSet& explicit_hosts, - const URLPatternSet& scriptable_hosts); + void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params); void OnUpdateTabSpecificPermissions(int page_id, int tab_id, const std::string& extension_id, diff --git a/extensions/DEPS b/extensions/DEPS index cd158fe..2b0dd51 100644 --- a/extensions/DEPS +++ b/extensions/DEPS @@ -14,4 +14,7 @@ specific_include_rules = { "^api_permission_set_unittest\.cc$": [ "+chrome/common/extensions/extension_messages.h", ], + "^manifest_permission_set_unittest\.cc$": [ + "+chrome/common/extensions/extension_messages.h", + ], } diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h index 594e75b..fb6aba8 100644 --- a/extensions/common/extensions_client.h +++ b/extensions/common/extensions_client.h @@ -16,6 +16,7 @@ namespace extensions { class APIPermissionSet; class Extension; class FeatureProvider; +class ManifestPermissionSet; class PermissionMessage; class PermissionMessageProvider; class PermissionsProvider; diff --git a/extensions/common/manifest_handler.cc b/extensions/common/manifest_handler.cc index 43e6bd8..944389d 100644 --- a/extensions/common/manifest_handler.cc +++ b/extensions/common/manifest_handler.cc @@ -9,6 +9,8 @@ #include "base/logging.h" #include "base/stl_util.h" #include "chrome/common/extensions/extension.h" +#include "extensions/common/permissions/manifest_permission.h" +#include "extensions/common/permissions/manifest_permission_set.h" namespace extensions { @@ -57,6 +59,15 @@ void ManifestHandler::Register() { GetRegistry()->RegisterManifestHandler(keys[i], this_linked); } +ManifestPermission* ManifestHandler::CreatePermission() { + return NULL; +} + +ManifestPermission* ManifestHandler::CreateInitialRequiredPermission( + const Extension* extension) { + return NULL; +} + // static void ManifestHandler::FinalizeRegistration() { GetRegistry()->Finalize(); @@ -80,6 +91,18 @@ bool ManifestHandler::ValidateExtension(const Extension* extension, } // static +ManifestPermission* ManifestHandler::CreatePermission(const std::string& name) { + return GetRegistry()->CreatePermission(name); +} + +// static +void ManifestHandler::AddExtensionInitialRequiredPermissions( + const Extension* extension, ManifestPermissionSet* permission_set) { + return GetRegistry()->AddExtensionInitialRequiredPermissions(extension, + permission_set); +} + +// static const std::vector<std::string> ManifestHandler::SingleKey( const std::string& key) { return std::vector<std::string>(1, key); @@ -144,6 +167,27 @@ bool ManifestHandlerRegistry::ValidateExtension( return true; } +ManifestPermission* ManifestHandlerRegistry::CreatePermission( + const std::string& name) { + ManifestHandlerMap::const_iterator it = handlers_.find(name); + if (it == handlers_.end()) + return NULL; + + return it->second->CreatePermission(); +} + +void ManifestHandlerRegistry::AddExtensionInitialRequiredPermissions( + const Extension* extension, ManifestPermissionSet* permission_set) { + for (ManifestHandlerMap::const_iterator it = handlers_.begin(); + it != handlers_.end(); ++it) { + ManifestPermission* permission = + it->second->CreateInitialRequiredPermission(extension); + if (permission) { + permission_set->insert(permission); + } + } +} + // static ManifestHandlerRegistry* ManifestHandlerRegistry::SetForTesting( ManifestHandlerRegistry* new_registry) { diff --git a/extensions/common/manifest_handler.h b/extensions/common/manifest_handler.h index fd7b571..90fbe84 100644 --- a/extensions/common/manifest_handler.h +++ b/extensions/common/manifest_handler.h @@ -16,6 +16,8 @@ namespace extensions { class Extension; +class ManifestPermission; +class ManifestPermissionSet; // An interface for clients that recognize and parse keys in extension // manifests. @@ -64,6 +66,19 @@ class ManifestHandler { // (new MyManifestHandler)->Register(); void Register(); + // Creates a |ManifestPermission| instance for the given manifest key |name|. + // The returned permission does not contain any permission data, so this + // method is usually used before calling |FromValue| or |Read|. Returns + // |NULL| if the manifest handler does not support custom permissions. + virtual ManifestPermission* CreatePermission(); + + // Creates a |ManifestPermission| instance containing the initial set of + // required manifest permissions for the given |extension|. Returns |NULL| if + // the manifest handler does not support custom permissions or if there was + // no manifest key in the extension manifest for this handler. + virtual ManifestPermission* CreateInitialRequiredPermission( + const Extension* extension); + // Calling FinalizeRegistration indicates that there are no more // manifest handlers to be registered. static void FinalizeRegistration(); @@ -79,6 +94,20 @@ class ManifestHandler { std::string* error, std::vector<InstallWarning>* warnings); + // Calls |CreatePermission| on the manifest handler for |key|. Returns |NULL| + // if there is no manifest handler for |key| or if the manifest handler for + // |key| does not support custom permissions. + static ManifestPermission* CreatePermission(const std::string& key); + + // Calls |CreateInitialRequiredPermission| on all registered manifest handlers + // and adds the returned permissions to |permission_set|. Note this should be + // called after all manifest data elements have been read, parsed and stored + // in the manifest data property of |extension|, as manifest handlers need + // access to their manifest data to initialize their required manifest + // permission. + static void AddExtensionInitialRequiredPermissions( + const Extension* extension, ManifestPermissionSet* permission_set); + protected: // A convenience method for handlers that only register for 1 key, // so that they can define keys() { return SingleKey(kKey); } @@ -108,6 +137,12 @@ class ManifestHandlerRegistry { std::string* error, std::vector<InstallWarning>* warnings); + ManifestPermission* CreatePermission(const std::string& key); + + void AddExtensionInitialRequiredPermissions( + const Extension* extension, + ManifestPermissionSet* permission_set); + // Overrides the current global ManifestHandlerRegistry with // |registry|, returning the current one. static ManifestHandlerRegistry* SetForTesting( diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc index ebaf8d8..0ed7769 100644 --- a/extensions/common/permissions/api_permission_set.cc +++ b/extensions/common/permissions/api_permission_set.cc @@ -109,51 +109,6 @@ bool ParseChildPermissions(const std::string& base_name, } // namespace -APIPermissionSet::APIPermissionSet() { -} - -APIPermissionSet::APIPermissionSet(const APIPermissionSet& set) { - this->operator=(set); -} - -APIPermissionSet::~APIPermissionSet() { -} - -APIPermissionSet::const_iterator::const_iterator( - const APIPermissionMap::const_iterator& it) - : it_(it) { -} - -APIPermissionSet::const_iterator::const_iterator( - const const_iterator& ids_it) - : it_(ids_it.it_) { -} - -APIPermissionSet& APIPermissionSet::operator=(const APIPermissionSet& rhs) { - const_iterator it = rhs.begin(); - const const_iterator end = rhs.end(); - while (it != end) { - insert(it->Clone()); - ++it; - } - return *this; -} - -bool APIPermissionSet::operator==(const APIPermissionSet& rhs) const { - const_iterator it = begin(); - const_iterator rhs_it = rhs.begin(); - const_iterator it_end = end(); - const_iterator rhs_it_end = rhs.end(); - - while (it != it_end && rhs_it != rhs_it_end) { - if (!it->Equal(*rhs_it)) - return false; - ++it; - ++rhs_it; - } - return it == it_end && rhs_it == rhs_it_end; -} - void APIPermissionSet::insert(APIPermission::ID id) { const APIPermissionInfo* permission_info = PermissionsInfo::GetInstance()->GetByID(id); @@ -161,130 +116,7 @@ void APIPermissionSet::insert(APIPermission::ID id) { } void APIPermissionSet::insert(APIPermission* permission) { - map_[permission->id()].reset(permission); -} - -bool APIPermissionSet::Contains(const APIPermissionSet& rhs) const { - APIPermissionSet::const_iterator it1 = begin(); - APIPermissionSet::const_iterator it2 = rhs.begin(); - APIPermissionSet::const_iterator end1 = end(); - APIPermissionSet::const_iterator end2 = rhs.end(); - - while (it1 != end1 && it2 != end2) { - if (it1->id() > it2->id()) { - return false; - } else if (it1->id() < it2->id()) { - ++it1; - } else { - if (!it1->Contains(*it2)) - return false; - ++it1; - ++it2; - } - } - - return it2 == end2; -} - -void APIPermissionSet::Difference( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3) { - CHECK(set3); - set3->clear(); - - APIPermissionSet::const_iterator it1 = set1.begin(); - APIPermissionSet::const_iterator it2 = set2.begin(); - const APIPermissionSet::const_iterator end1 = set1.end(); - const APIPermissionSet::const_iterator end2 = set2.end(); - - while (it1 != end1 && it2 != end2) { - if (it1->id() < it2->id()) { - set3->insert(it1->Clone()); - ++it1; - } else if (it1->id() > it2->id()) { - ++it2; - } else { - APIPermission* p = it1->Diff(*it2); - if (p) - set3->insert(p); - ++it1; - ++it2; - } - } - - while (it1 != end1) { - set3->insert(it1->Clone()); - ++it1; - } -} - -void APIPermissionSet::Intersection( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3) { - DCHECK(set3); - set3->clear(); - - APIPermissionSet::const_iterator it1 = set1.begin(); - APIPermissionSet::const_iterator it2 = set2.begin(); - const APIPermissionSet::const_iterator end1 = set1.end(); - const APIPermissionSet::const_iterator end2 = set2.end(); - - while (it1 != end1 && it2 != end2) { - if (it1->id() < it2->id()) { - ++it1; - } else if (it1->id() > it2->id()) { - ++it2; - } else { - APIPermission* p = it1->Intersect(*it2); - if (p) - set3->insert(p); - ++it1; - ++it2; - } - } -} - -void APIPermissionSet::Union( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3) { - DCHECK(set3); - set3->clear(); - - APIPermissionSet::const_iterator it1 = set1.begin(); - APIPermissionSet::const_iterator it2 = set2.begin(); - const APIPermissionSet::const_iterator end1 = set1.end(); - const APIPermissionSet::const_iterator end2 = set2.end(); - - while (true) { - if (it1 == end1) { - while (it2 != end2) { - set3->insert(it2->Clone()); - ++it2; - } - break; - } - if (it2 == end2) { - while (it1 != end1) { - set3->insert(it1->Clone()); - ++it1; - } - break; - } - if (it1->id() < it2->id()) { - set3->insert(it1->Clone()); - ++it1; - } else if (it1->id() > it2->id()) { - set3->insert(it2->Clone()); - ++it2; - } else { - set3->insert(it1->Union(*it2)); - ++it1; - ++it2; - } - } + BaseSetOperators<APIPermissionSet>::insert(permission); } // static @@ -334,8 +166,8 @@ void APIPermissionSet::AddImpliedPermissions() { // The fileSystem.write and fileSystem.directory permissions imply // fileSystem.writeDirectory. // TODO(sammc): Remove this. See http://crbug.com/284849. - if (ContainsKey(map_, APIPermission::kFileSystemWrite) && - ContainsKey(map_, APIPermission::kFileSystemDirectory)) { + if (ContainsKey(map(), APIPermission::kFileSystemWrite) && + ContainsKey(map(), APIPermission::kFileSystemDirectory)) { insert(APIPermission::kFileSystemWriteDirectory); } } diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h index 9b6f26d..c5dd248 100644 --- a/extensions/common/permissions/api_permission_set.h +++ b/extensions/common/permissions/api_permission_set.h @@ -5,11 +5,9 @@ #ifndef EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ #define EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ -#include <iterator> -#include <map> -#include "base/memory/linked_ptr.h" #include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/base_set_operators.h" namespace base { class ListValue; @@ -18,48 +16,16 @@ class ListValue; namespace extensions { class Extension; +class APIPermissionSet; -typedef std::map<APIPermission::ID, - linked_ptr<APIPermission> > APIPermissionMap; +template<> +struct BaseSetOperatorsTraits<APIPermissionSet> { + typedef APIPermission ElementType; + typedef APIPermission::ID ElementIDType; +}; -class APIPermissionSet { +class APIPermissionSet : public BaseSetOperators<APIPermissionSet> { public: - class const_iterator : - public std::iterator<std::input_iterator_tag, const APIPermission*> { - public: - const_iterator(const APIPermissionMap::const_iterator& it); - const_iterator(const const_iterator& ids_it); - - const_iterator& operator++() { - ++it_; - return *this; - } - - const_iterator operator++(int) { - const_iterator tmp(it_++); - return tmp; - } - - bool operator==(const const_iterator& rhs) const { - return it_ == rhs.it_; - } - - bool operator!=(const const_iterator& rhs) const { - return it_ != rhs.it_; - } - - const APIPermission* operator*() const { - return it_->second.get(); - } - - const APIPermission* operator->() const { - return it_->second.get(); - } - - private: - APIPermissionMap::const_iterator it_; - }; - enum ParseSource { // Don't allow internal permissions to be parsed (e.g. entries in the // "permissions" list in a manifest). @@ -70,87 +36,16 @@ class APIPermissionSet { kAllowInternalPermissions, }; - APIPermissionSet(); - - APIPermissionSet(const APIPermissionSet& set); - - ~APIPermissionSet(); - - const_iterator begin() const { - return const_iterator(map().begin()); - } - - const_iterator end() const { - return map().end(); - } - - const_iterator find(APIPermission::ID id) const { - return map().find(id); - } - - const APIPermissionMap& map() const { - return map_; - } - - APIPermissionMap& map() { - return map_; - } - - void clear() { - map_.clear(); - } - - size_t count(APIPermission::ID id) const { - return map().count(id); - } - - bool empty() const { - return map().empty(); - } - - size_t erase(APIPermission::ID id) { - return map().erase(id); - } - - size_t size() const { - return map().size(); - } - - APIPermissionSet& operator=(const APIPermissionSet& rhs); - - bool operator==(const APIPermissionSet& rhs) const; - - bool operator!=(const APIPermissionSet& rhs) const { - return !operator==(rhs); - } - void insert(APIPermission::ID id); // Insert |permission| into the APIPermissionSet. The APIPermissionSet will // take the ownership of |permission|, void insert(APIPermission* permission); - bool Contains(const APIPermissionSet& rhs) const; - - static void Difference( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3); - - static void Intersection( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3); - - static void Union( - const APIPermissionSet& set1, - const APIPermissionSet& set2, - APIPermissionSet* set3); - // Parses permissions from |permissions| and adds the parsed permissions to // |api_permissions|. If |source| is kDisallowInternalPermissions, treat - // permissions with kFlagInternal as errors. If |unhandled_permissions| - // is not NULL, the names of all permissions that couldn't be parsed will be + // permissions with kFlagInternal as errors. If |unhandled_permissions| is + // not NULL, the names of all permissions that couldn't be parsed will be // added to this vector. If |error| is NULL, parsing will continue with the // next permission if invalid data is detected. If |error| is not NULL, it // will be set to an error message and false is returned when an invalid @@ -163,9 +58,6 @@ class APIPermissionSet { std::vector<std::string>* unhandled_permissions); void AddImpliedPermissions(); - - private: - APIPermissionMap map_; }; } // namespace extensions diff --git a/extensions/common/permissions/base_set_operators.h b/extensions/common/permissions/base_set_operators.h new file mode 100644 index 0000000..38ff091 --- /dev/null +++ b/extensions/common/permissions/base_set_operators.h @@ -0,0 +1,288 @@ +// Copyright 2013 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_BASE_SET_OPERATORS_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_BASE_SET_OPERATORS_H_ + +#include <iterator> +#include <map> + +#include "base/memory/linked_ptr.h" +#include "base/template_util.h" + +namespace extensions { + +// Traits for template paramater of |BaseSetOperators<T>|. Specializations +// should define |ElementType| for the type of elements to store in the set, +// and |EmementIDType| for the type of element identifiers. +template <typename T> +struct BaseSetOperatorsTraits {}; + +// Set operations shared by |APIPermissionSet| and |ManifestPermissionSet|. +// +// TODO(rpaquay): It would be nice to remove the need for the sub-classes and +// instead directly use this class where needed. +template <typename T> +class BaseSetOperators { + public: + typedef typename BaseSetOperatorsTraits<T>::ElementType ElementType; + typedef typename BaseSetOperatorsTraits<T>::ElementIDType ElementIDType; + typedef std::map<ElementIDType, linked_ptr<ElementType> > Map; + + class const_iterator : + public std::iterator<std::input_iterator_tag, const ElementType*> { + public: + const_iterator(const typename Map::const_iterator& it) : it_(it) {} + const_iterator(const const_iterator& ids_it) : it_(ids_it.it_) {} + + const_iterator& operator++() { + ++it_; + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp(it_++); + return tmp; + } + + bool operator==(const const_iterator& rhs) const { + return it_ == rhs.it_; + } + + bool operator!=(const const_iterator& rhs) const { + return it_ != rhs.it_; + } + + const ElementType* operator*() const { + return it_->second.get(); + } + + const ElementType* operator->() const { + return it_->second.get(); + } + + private: + typename Map::const_iterator it_; + }; + + BaseSetOperators() { + // Ensure |T| is convertible to us, so we can safely downcast when calling + // methods that must exist in |T|. + COMPILE_ASSERT((base::is_convertible<T*, BaseSetOperators<T>*>::value), + U_ptr_must_implicitly_convert_to_T_ptr); + } + + BaseSetOperators(const T& set) { + this->operator=(set); + } + + ~BaseSetOperators() {} + + T& operator=(const T& rhs) { + return Assign(rhs); + } + + bool operator==(const T& rhs) const { + return Equal(rhs); + } + + bool operator!=(const T& rhs) const { + return !operator==(rhs); + } + + T& Assign(const T& rhs) { + const_iterator it = rhs.begin(); + const const_iterator end = rhs.end(); + while (it != end) { + insert(it->Clone()); + ++it; + } + return *static_cast<T*>(this); + } + + bool Equal(const T& rhs) const { + const_iterator it = begin(); + const_iterator rhs_it = rhs.begin(); + const_iterator it_end = end(); + const_iterator rhs_it_end = rhs.end(); + + while (it != it_end && rhs_it != rhs_it_end) { + if (it->id() > rhs_it->id()) + return false; + else if (it->id() < rhs_it->id()) + return false; + else if (!it->Equal(*rhs_it)) + return false; + ++it; + ++rhs_it; + } + return it == it_end && rhs_it == rhs_it_end; + } + + bool Contains(const T& rhs) const { + const_iterator it1 = begin(); + const_iterator it2 = rhs.begin(); + const_iterator end1 = end(); + const_iterator end2 = rhs.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() > it2->id()) { + return false; + } else if (it1->id() < it2->id()) { + ++it1; + } else { + if (!it1->Contains(*it2)) + return false; + ++it1; + ++it2; + } + } + + return it2 == end2; + } + + static void Difference(const T& set1, const T& set2, T* set3) { + CHECK(set3); + set3->clear(); + + const_iterator it1 = set1.begin(); + const_iterator it2 = set2.begin(); + const const_iterator end1 = set1.end(); + const const_iterator end2 = set2.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() < it2->id()) { + set3->insert(it1->Clone()); + ++it1; + } else if (it1->id() > it2->id()) { + ++it2; + } else { + ElementType* p = it1->Diff(*it2); + if (p) + set3->insert(p); + ++it1; + ++it2; + } + } + + while (it1 != end1) { + set3->insert(it1->Clone()); + ++it1; + } + } + + static void Intersection(const T& set1, const T& set2, T* set3) { + DCHECK(set3); + set3->clear(); + + const_iterator it1 = set1.begin(); + const_iterator it2 = set2.begin(); + const const_iterator end1 = set1.end(); + const const_iterator end2 = set2.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() < it2->id()) { + ++it1; + } else if (it1->id() > it2->id()) { + ++it2; + } else { + ElementType* p = it1->Intersect(*it2); + if (p) + set3->insert(p); + ++it1; + ++it2; + } + } + } + + static void Union(const T& set1, const T& set2, T* set3) { + DCHECK(set3); + set3->clear(); + + const_iterator it1 = set1.begin(); + const_iterator it2 = set2.begin(); + const const_iterator end1 = set1.end(); + const const_iterator end2 = set2.end(); + + while (true) { + if (it1 == end1) { + while (it2 != end2) { + set3->insert(it2->Clone()); + ++it2; + } + break; + } + if (it2 == end2) { + while (it1 != end1) { + set3->insert(it1->Clone()); + ++it1; + } + break; + } + if (it1->id() < it2->id()) { + set3->insert(it1->Clone()); + ++it1; + } else if (it1->id() > it2->id()) { + set3->insert(it2->Clone()); + ++it2; + } else { + set3->insert(it1->Union(*it2)); + ++it1; + ++it2; + } + } + } + + const_iterator begin() const { + return const_iterator(map().begin()); + } + + const_iterator end() const { + return map().end(); + } + + const_iterator find(ElementIDType id) const { + return map().find(id); + } + + size_t count(ElementIDType id) const { + return map().count(id); + } + + bool empty() const { + return map().empty(); + } + + size_t erase(ElementIDType id) { + return map().erase(id); + } + + // Take ownership and insert |item| into the set. + void insert(ElementType* item) { + map_[item->id()].reset(item); + } + + size_t size() const { + return map().size(); + } + + const Map& map() const { + return map_; + } + + Map& map() { + return map_; + } + + void clear() { + map_.clear(); + } + + private: + Map map_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_BASE_SET_OPERATORS_H_ diff --git a/extensions/common/permissions/manifest_permission.cc b/extensions/common/permissions/manifest_permission.cc new file mode 100644 index 0000000..b15c541 --- /dev/null +++ b/extensions/common/permissions/manifest_permission.cc @@ -0,0 +1,13 @@ +// Copyright 2013 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/permissions/manifest_permission.h" + +namespace extensions { + +ManifestPermission::ManifestPermission() {} + +ManifestPermission::~ManifestPermission() { } + +} // namespace extensions diff --git a/extensions/common/permissions/manifest_permission.h b/extensions/common/permissions/manifest_permission.h new file mode 100644 index 0000000..af9e035 --- /dev/null +++ b/extensions/common/permissions/manifest_permission.h @@ -0,0 +1,88 @@ +// Copyright 2013 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/pickle.h" +#include "extensions/common/permissions/permission_message.h" + +class PickleIterator; + +namespace base { +class Value; +class ListValue; +} + +namespace IPC { +class Message; +} + +namespace extensions { + +// Represent the custom behavior of a top-level manifest entry contributing to +// permission messages and storage. +class ManifestPermission { + public: + ManifestPermission(); + virtual ~ManifestPermission(); + + // The manifest key this permission applies to. + virtual std::string name() const = 0; + + // Same as name(), needed for compatibility with APIPermission. + virtual std::string id() const = 0; + + // Returns true if this permission has any PermissionMessages. + virtual bool HasMessages() const = 0; + + // Returns the localized permission messages of this permission. + virtual PermissionMessages GetMessages() const = 0; + + // Parses the ManifestPermission from |value|. Returns false if error happens. + virtual bool FromValue(const base::Value* value) = 0; + + // Stores this into a new created Value. + virtual scoped_ptr<base::Value> ToValue() const = 0; + + // Clones this. + virtual ManifestPermission* Clone() const = 0; + + // Returns a new manifest permission which equals this - |rhs|. + virtual ManifestPermission* Diff(const ManifestPermission* rhs) const = 0; + + // Returns a new manifest permission which equals the union of this and |rhs|. + virtual ManifestPermission* Union(const ManifestPermission* rhs) const = 0; + + // Returns a new manifest permission which equals the intersect of this and + // |rhs|. + virtual ManifestPermission* Intersect(const ManifestPermission* rhs) + const = 0; + + // Returns true if |rhs| is a subset of this. + virtual bool Contains(const ManifestPermission* rhs) const = 0; + + // Returns true if |rhs| is equal to this. + virtual bool Equal(const ManifestPermission* rhs) const = 0; + + // IPC functions + // Writes this into the given IPC message |m|. + virtual void Write(IPC::Message* m) const = 0; + + // Reads from the given IPC message |m|. + virtual bool Read(const IPC::Message* m, PickleIterator* iter) = 0; + + // Logs this permission. + virtual void Log(std::string* log) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ManifestPermission); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_H_ diff --git a/extensions/common/permissions/manifest_permission_set.cc b/extensions/common/permissions/manifest_permission_set.cc new file mode 100644 index 0000000..821159f --- /dev/null +++ b/extensions/common/permissions/manifest_permission_set.cc @@ -0,0 +1,93 @@ +// Copyright 2013 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/permissions/manifest_permission_set.h" + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handler.h" +#include "extensions/common/permissions/manifest_permission.h" + +namespace { + +using extensions::ErrorUtils; +using extensions::ManifestPermission; +using extensions::ManifestPermissionSet; +using extensions::ManifestHandler; +namespace errors = extensions::manifest_errors; + +bool CreateManifestPermission( + const std::string& permission_name, + const base::Value* permission_value, + ManifestPermissionSet* manifest_permissions, + string16* error, + std::vector<std::string>* unhandled_permissions) { + + scoped_ptr<ManifestPermission> permission( + ManifestHandler::CreatePermission(permission_name)); + + if (!permission) { + if (unhandled_permissions) + unhandled_permissions->push_back(permission_name); + else + LOG(WARNING) << "Unknown permission[" << permission_name << "]."; + return true; + } + + if (!permission->FromValue(permission_value)) { + if (error) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidPermission, permission_name); + return false; + } + LOG(WARNING) << "Parse permission failed."; + return true; + } else { + manifest_permissions->insert(permission.release()); + return true; + } +} + +} // namespace + +namespace extensions { + +// static +bool ManifestPermissionSet::ParseFromJSON( + const base::ListValue* permissions, + ManifestPermissionSet* manifest_permissions, + string16* error, + std::vector<std::string>* unhandled_permissions) { + for (size_t i = 0; i < permissions->GetSize(); ++i) { + std::string permission_name; + const base::Value* permission_value = NULL; + if (!permissions->GetString(i, &permission_name)) { + const base::DictionaryValue* dict = NULL; + // permission should be a string or a single key dict. + if (!permissions->GetDictionary(i, &dict) || dict->size() != 1) { + if (error) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidPermission, base::IntToString(i)); + return false; + } + LOG(WARNING) << "Permission is not a string or single key dict."; + continue; + } + base::DictionaryValue::Iterator it(*dict); + permission_name = it.key(); + permission_value = &it.value(); + } + + if (!CreateManifestPermission(permission_name, permission_value, + manifest_permissions, error, + unhandled_permissions)) + return false; + } + return true; +} + +} // namespace extensions diff --git a/extensions/common/permissions/manifest_permission_set.h b/extensions/common/permissions/manifest_permission_set.h new file mode 100644 index 0000000..e9683bc --- /dev/null +++ b/extensions/common/permissions/manifest_permission_set.h @@ -0,0 +1,47 @@ +// Copyright 2013 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_SET_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_SET_H_ + +#include <string> +#include <vector> + +#include "base/strings/string16.h" +#include "extensions/common/permissions/base_set_operators.h" + +namespace base { +class ListValue; +} // namespace base + +namespace extensions { + +class Extension; +class ManifestPermission; +class ManifestPermissionSet; + +template<> +struct BaseSetOperatorsTraits<ManifestPermissionSet> { + typedef ManifestPermission ElementType; + typedef std::string ElementIDType; +}; + +class ManifestPermissionSet : public BaseSetOperators<ManifestPermissionSet> { + public: + // Parses permissions from |permissions| and adds the parsed permissions to + // |manifest_permissions|. If |unhandled_permissions| is not NULL, the names + // of all permissions that couldn't be parsed will be added to this vector. + // If |error| is NULL, parsing will continue with the next permission if + // invalid data is detected. If |error| is not NULL, it will be set to an + // error message and false is returned when an invalid permission is found. + static bool ParseFromJSON( + const base::ListValue* permissions, + ManifestPermissionSet* manifest_permissions, + string16* error, + std::vector<std::string>* unhandled_permissions); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_MANIFEST_PERMISSION_SET_H_ diff --git a/extensions/common/permissions/manifest_permission_set_unittest.cc b/extensions/common/permissions/manifest_permission_set_unittest.cc new file mode 100644 index 0000000..a0191c3 --- /dev/null +++ b/extensions/common/permissions/manifest_permission_set_unittest.cc @@ -0,0 +1,272 @@ +// Copyright 2013 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 "base/pickle.h" +#include "base/values.h" +#include "chrome/common/extensions/extension_messages.h" +#include "extensions/common/permissions/manifest_permission.h" +#include "extensions/common/permissions/manifest_permission_set.h" +#include "ipc/ipc_message.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +class MockManifestPermission : public ManifestPermission { + public: + MockManifestPermission(const std::string& name) + : name_(name) { + } + + virtual std::string name() const OVERRIDE { + return name_; + } + + virtual std::string id() const OVERRIDE { + return name(); + } + + virtual bool HasMessages() const OVERRIDE { + return false; + } + + virtual PermissionMessages GetMessages() const OVERRIDE { + return PermissionMessages(); + } + + virtual bool FromValue(const base::Value* value) OVERRIDE { + return false; + } + + virtual scoped_ptr<base::Value> ToValue() const OVERRIDE { + return scoped_ptr<base::Value>(base::Value::CreateNullValue()); + } + + virtual ManifestPermission* Clone() const OVERRIDE { + return new MockManifestPermission(name_); + } + + virtual ManifestPermission* Diff(const ManifestPermission* rhs) + const OVERRIDE { + const MockManifestPermission* other = + static_cast<const MockManifestPermission*>(rhs); + EXPECT_EQ(name_, other->name_); + return NULL; + } + + virtual ManifestPermission* Union(const ManifestPermission* rhs) + const OVERRIDE { + const MockManifestPermission* other = + static_cast<const MockManifestPermission*>(rhs); + EXPECT_EQ(name_, other->name_); + return new MockManifestPermission(name_); + } + + virtual ManifestPermission* Intersect(const ManifestPermission* rhs) + const OVERRIDE { + const MockManifestPermission* other = + static_cast<const MockManifestPermission*>(rhs); + EXPECT_EQ(name_, other->name_); + return new MockManifestPermission(name_); + } + + virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE { + const MockManifestPermission* other = + static_cast<const MockManifestPermission*>(rhs); + EXPECT_EQ(name_, other->name_); + return true; + } + + virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE { + const MockManifestPermission* other = + static_cast<const MockManifestPermission*>(rhs); + EXPECT_EQ(name_, other->name_); + return true; + } + + virtual void Write(IPC::Message* m) const OVERRIDE { + IPC::WriteParam(m, name_); + } + + virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE { + std::string read_name; + bool result = IPC::ReadParam(m, iter, &read_name); + if (!result) + return result; + EXPECT_EQ(read_name, name_); + return true; + } + + virtual void Log(std::string* log) const OVERRIDE { + } + + private: + std::string name_; +}; + +TEST(ManifestPermissionSetTest, General) { + ManifestPermissionSet set; + set.insert(new MockManifestPermission("p1")); + set.insert(new MockManifestPermission("p2")); + set.insert(new MockManifestPermission("p3")); + set.insert(new MockManifestPermission("p4")); + set.insert(new MockManifestPermission("p5")); + + EXPECT_EQ(set.find("p1")->id(), "p1"); + EXPECT_TRUE(set.find("p10") == set.end()); + + EXPECT_EQ(set.size(), 5u); + + EXPECT_EQ(set.erase("p1"), 1u); + EXPECT_EQ(set.size(), 4u); + + EXPECT_EQ(set.erase("p1"), 0u); + EXPECT_EQ(set.size(), 4u); +} + +TEST(ManifestPermissionSetTest, CreateUnion) { + ManifestPermissionSet permissions1; + ManifestPermissionSet permissions2; + ManifestPermissionSet expected_permissions; + ManifestPermissionSet result; + + ManifestPermission* permission = new MockManifestPermission("p3"); + + // Union with an empty set. + permissions1.insert(new MockManifestPermission("p1")); + permissions1.insert(new MockManifestPermission("p2")); + permissions1.insert(permission->Clone()); + expected_permissions.insert(new MockManifestPermission("p1")); + expected_permissions.insert(new MockManifestPermission("p2")); + expected_permissions.insert(permission); + + ManifestPermissionSet::Union(permissions1, permissions2, &result); + + EXPECT_TRUE(permissions1.Contains(permissions2)); + EXPECT_TRUE(permissions1.Contains(result)); + EXPECT_FALSE(permissions2.Contains(permissions1)); + EXPECT_FALSE(permissions2.Contains(result)); + EXPECT_TRUE(result.Contains(permissions1)); + EXPECT_TRUE(result.Contains(permissions2)); + + EXPECT_EQ(expected_permissions, result); + + // Now use a real second set. + permissions2.insert(new MockManifestPermission("p1")); + permissions2.insert(new MockManifestPermission("p2")); + permissions2.insert(new MockManifestPermission("p33")); + permissions2.insert(new MockManifestPermission("p4")); + permissions2.insert(new MockManifestPermission("p5")); + + expected_permissions.insert(new MockManifestPermission("p1")); + expected_permissions.insert(new MockManifestPermission("p2")); + expected_permissions.insert(new MockManifestPermission("p3")); + expected_permissions.insert(new MockManifestPermission("p4")); + expected_permissions.insert(new MockManifestPermission("p5")); + expected_permissions.insert(new MockManifestPermission("p33")); + + ManifestPermissionSet::Union(permissions1, permissions2, &result); + + { + ManifestPermissionSet set1; + set1.insert(new MockManifestPermission("p1")); + set1.insert(new MockManifestPermission("p2")); + ManifestPermissionSet set2; + set2.insert(new MockManifestPermission("p3")); + + EXPECT_FALSE(set1.Contains(set2)); + EXPECT_FALSE(set2.Contains(set1)); + } + + EXPECT_FALSE(permissions1.Contains(permissions2)); + EXPECT_FALSE(permissions1.Contains(result)); + EXPECT_FALSE(permissions2.Contains(permissions1)); + EXPECT_FALSE(permissions2.Contains(result)); + EXPECT_TRUE(result.Contains(permissions1)); + EXPECT_TRUE(result.Contains(permissions2)); + + EXPECT_EQ(expected_permissions, result); +} + +TEST(ManifestPermissionSetTest, CreateIntersection) { + ManifestPermissionSet permissions1; + ManifestPermissionSet permissions2; + ManifestPermissionSet expected_permissions; + ManifestPermissionSet result; + + // Intersection with an empty set. + permissions1.insert(new MockManifestPermission("p1")); + permissions1.insert(new MockManifestPermission("p2")); + permissions1.insert(new MockManifestPermission("p3")); + + ManifestPermissionSet::Intersection(permissions1, permissions2, &result); + EXPECT_TRUE(permissions1.Contains(result)); + EXPECT_TRUE(permissions2.Contains(result)); + EXPECT_TRUE(permissions1.Contains(permissions2)); + EXPECT_FALSE(permissions2.Contains(permissions1)); + EXPECT_FALSE(result.Contains(permissions1)); + EXPECT_TRUE(result.Contains(permissions2)); + + EXPECT_TRUE(result.empty()); + EXPECT_EQ(expected_permissions, result); + + // Now use a real second set. + permissions2.insert(new MockManifestPermission("p1")); + permissions2.insert(new MockManifestPermission("p3")); + permissions2.insert(new MockManifestPermission("p4")); + permissions2.insert(new MockManifestPermission("p5")); + + expected_permissions.insert(new MockManifestPermission("p1")); + expected_permissions.insert(new MockManifestPermission("p3")); + + ManifestPermissionSet::Intersection(permissions1, permissions2, &result); + + EXPECT_TRUE(permissions1.Contains(result)); + EXPECT_TRUE(permissions2.Contains(result)); + EXPECT_FALSE(permissions1.Contains(permissions2)); + EXPECT_FALSE(permissions2.Contains(permissions1)); + EXPECT_FALSE(result.Contains(permissions1)); + EXPECT_FALSE(result.Contains(permissions2)); + + EXPECT_EQ(expected_permissions, result); +} + +TEST(ManifestPermissionSetTest, CreateDifference) { + ManifestPermissionSet permissions1; + ManifestPermissionSet permissions2; + ManifestPermissionSet expected_permissions; + ManifestPermissionSet result; + + // Difference with an empty set. + permissions1.insert(new MockManifestPermission("p1")); + permissions1.insert(new MockManifestPermission("p2")); + permissions1.insert(new MockManifestPermission("p3")); + + ManifestPermissionSet::Difference(permissions1, permissions2, &result); + + EXPECT_EQ(permissions1, result); + + // Now use a real second set. + permissions2.insert(new MockManifestPermission("p1")); + permissions2.insert(new MockManifestPermission("p2")); + permissions2.insert(new MockManifestPermission("p4")); + permissions2.insert(new MockManifestPermission("p5")); + permissions2.insert(new MockManifestPermission("p6")); + + expected_permissions.insert(new MockManifestPermission("p3")); + + ManifestPermissionSet::Difference(permissions1, permissions2, &result); + + EXPECT_TRUE(permissions1.Contains(result)); + EXPECT_FALSE(permissions2.Contains(result)); + + EXPECT_EQ(expected_permissions, result); + + // |result| = |permissions1| - |permissions2| --> + // |result| intersect |permissions2| == empty_set + ManifestPermissionSet result2; + ManifestPermissionSet::Intersection(result, permissions2, &result2); + EXPECT_TRUE(result2.empty()); +} + +} // namespace extensions diff --git a/extensions/common/permissions/permission_set.cc b/extensions/common/permissions/permission_set.cc index d3f46fc..8ec1cbe 100644 --- a/extensions/common/permissions/permission_set.cc +++ b/extensions/common/permissions/permission_set.cc @@ -38,9 +38,11 @@ PermissionSet::PermissionSet() {} PermissionSet::PermissionSet( const APIPermissionSet& apis, + const ManifestPermissionSet& manifest_permissions, const URLPatternSet& explicit_hosts, const URLPatternSet& scriptable_hosts) : apis_(apis), + manifest_permissions_(manifest_permissions), scriptable_hosts_(scriptable_hosts) { AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); InitImplicitPermissions(); @@ -58,6 +60,11 @@ PermissionSet* PermissionSet::CreateDifference( APIPermissionSet apis; APIPermissionSet::Difference(set1_safe->apis(), set2_safe->apis(), &apis); + ManifestPermissionSet manifest_permissions; + ManifestPermissionSet::Difference(set1_safe->manifest_permissions(), + set2_safe->manifest_permissions(), + &manifest_permissions); + URLPatternSet explicit_hosts; URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), set2_safe->explicit_hosts(), @@ -68,7 +75,8 @@ PermissionSet* PermissionSet::CreateDifference( set2_safe->scriptable_hosts(), &scriptable_hosts); - return new PermissionSet(apis, explicit_hosts, scriptable_hosts); + return new PermissionSet(apis, manifest_permissions, + explicit_hosts, scriptable_hosts); } // static @@ -82,6 +90,11 @@ PermissionSet* PermissionSet::CreateIntersection( APIPermissionSet apis; APIPermissionSet::Intersection(set1_safe->apis(), set2_safe->apis(), &apis); + ManifestPermissionSet manifest_permissions; + ManifestPermissionSet::Intersection(set1_safe->manifest_permissions(), + set2_safe->manifest_permissions(), + &manifest_permissions); + URLPatternSet explicit_hosts; URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), set2_safe->explicit_hosts(), @@ -92,7 +105,8 @@ PermissionSet* PermissionSet::CreateIntersection( set2_safe->scriptable_hosts(), &scriptable_hosts); - return new PermissionSet(apis, explicit_hosts, scriptable_hosts); + return new PermissionSet(apis, manifest_permissions, + explicit_hosts, scriptable_hosts); } // static @@ -106,6 +120,11 @@ PermissionSet* PermissionSet::CreateUnion( APIPermissionSet apis; APIPermissionSet::Union(set1_safe->apis(), set2_safe->apis(), &apis); + ManifestPermissionSet manifest_permissions; + ManifestPermissionSet::Union(set1_safe->manifest_permissions(), + set2_safe->manifest_permissions(), + &manifest_permissions); + URLPatternSet explicit_hosts; URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), set2_safe->explicit_hosts(), @@ -116,18 +135,21 @@ PermissionSet* PermissionSet::CreateUnion( set2_safe->scriptable_hosts(), &scriptable_hosts); - return new PermissionSet(apis, explicit_hosts, scriptable_hosts); + return new PermissionSet(apis, manifest_permissions, + explicit_hosts, scriptable_hosts); } bool PermissionSet::operator==( const PermissionSet& rhs) const { return apis_ == rhs.apis_ && + manifest_permissions_ == rhs.manifest_permissions_ && scriptable_hosts_ == rhs.scriptable_hosts_ && explicit_hosts_ == rhs.explicit_hosts_; } bool PermissionSet::Contains(const PermissionSet& set) const { return apis_.Contains(set.apis()) && + manifest_permissions_.Contains(set.manifest_permissions()) && explicit_hosts().Contains(set.explicit_hosts()) && scriptable_hosts().Contains(set.scriptable_hosts()); } @@ -147,7 +169,7 @@ bool PermissionSet::IsEmpty() const { return false; // Or if it has no api permissions. - return apis().empty(); + return apis().empty() && manifest_permissions().empty(); } bool PermissionSet::HasAPIPermission( diff --git a/extensions/common/permissions/permission_set.h b/extensions/common/permissions/permission_set.h index a0e4b34..9e6a7b9 100644 --- a/extensions/common/permissions/permission_set.h +++ b/extensions/common/permissions/permission_set.h @@ -17,6 +17,8 @@ #include "extensions/common/manifest.h" #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/permissions/manifest_permission.h" +#include "extensions/common/permissions/manifest_permission_set.h" #include "extensions/common/url_pattern_set.h" namespace extensions { @@ -32,10 +34,11 @@ class PermissionSet PermissionSet(); // Creates a new permission set based on the specified data: the API - // permissions, host permissions, and scriptable hosts. The effective hosts - // of the newly created permission set will be inferred from the given - // host permissions. + // permissions, manifest key permissions, host permissions, and scriptable + // hosts. The effective hosts of the newly created permission set will be + // inferred from the given host permissions. PermissionSet(const APIPermissionSet& apis, + const ManifestPermissionSet& manifest_permissions, const URLPatternSet& explicit_hosts, const URLPatternSet& scriptable_hosts); @@ -102,6 +105,10 @@ class PermissionSet const APIPermissionSet& apis() const { return apis_; } + const ManifestPermissionSet& manifest_permissions() const { + return manifest_permissions_; + } + const URLPatternSet& effective_hosts() const { return effective_hosts_; } const URLPatternSet& explicit_hosts() const { return explicit_hosts_; } @@ -126,6 +133,10 @@ class PermissionSet // extension APIs and features. APIPermissionSet apis_; + // The manifest key permission list is used when deciding if an extension + // can access certain extension APIs and features. + ManifestPermissionSet manifest_permissions_; + // The list of hosts that can be accessed directly from the extension. // TODO(jstritar): Rename to "hosts_"? URLPatternSet explicit_hosts_; diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 5053b9b..d673ac1 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -89,6 +89,11 @@ 'common/permissions/api_permission.h', 'common/permissions/api_permission_set.cc', 'common/permissions/api_permission_set.h', + 'common/permissions/base_set_operators.h', + 'common/permissions/manifest_permission.cc', + 'common/permissions/manifest_permission.h', + 'common/permissions/manifest_permission_set.cc', + 'common/permissions/manifest_permission_set.h', 'common/permissions/permission_message.cc', 'common/permissions/permission_message.h', 'common/permissions/permission_message_provider.cc', |