Commit 33de31fd authored by jstritar@chromium.org's avatar jstritar@chromium.org

Add origins to the extension permissions API.

BUG=48119,84507
TEST=*Extension*


Review URL: http://codereview.chromium.org/7508029

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95704 0039d316-1c4b-4281-b951-d872f2087c98
parent 93bbefaf
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#include "chrome/browser/extensions/extension_permissions_api.h" #include "chrome/browser/extensions/extension_permissions_api.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/stringprintf.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_permissions_api_constants.h" #include "chrome/browser/extensions/extension_permissions_api_constants.h"
...@@ -14,6 +13,7 @@ ...@@ -14,6 +13,7 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/extensions/extension_permission_set.h"
#include "chrome/common/extensions/url_pattern_set.h" #include "chrome/common/extensions/url_pattern_set.h"
...@@ -42,11 +42,14 @@ DictionaryValue* PackPermissionsToValue(const ExtensionPermissionSet* set) { ...@@ -42,11 +42,14 @@ DictionaryValue* PackPermissionsToValue(const ExtensionPermissionSet* set) {
i != set->apis().end(); ++i) i != set->apis().end(); ++i)
apis->Append(Value::CreateStringValue(info->GetByID(*i)->name())); apis->Append(Value::CreateStringValue(info->GetByID(*i)->name()));
// TODO(jstritar): Include hosts once the API supports them. At that point, // Generate the list of origin permissions.
// we could also shared this code with ExtensionPermissionSet methods in URLPatternSet hosts = set->explicit_hosts();
// ExtensionPrefs. ListValue* origins = new ListValue();
for (URLPatternSet::const_iterator i = hosts.begin(); i != hosts.end(); ++i)
origins->Append(Value::CreateStringValue(i->GetAsString()));
value->Set(keys::kApisKey, apis); value->Set(keys::kApisKey, apis);
value->Set(keys::kOriginsKey, origins);
return value; return value;
} }
...@@ -74,17 +77,43 @@ bool UnpackPermissionsFromValue(DictionaryValue* value, ...@@ -74,17 +77,43 @@ bool UnpackPermissionsFromValue(DictionaryValue* value,
ExtensionAPIPermission* permission = info->GetByName(api_name); ExtensionAPIPermission* permission = info->GetByName(api_name);
if (!permission) { if (!permission) {
*error = base::StringPrintf( *error = ExtensionErrorUtils::FormatErrorMessage(
keys::kUnknownPermissionError, api_name.c_str()); keys::kUnknownPermissionError, api_name);
return false; return false;
} }
apis.insert(permission->id()); apis.insert(permission->id());
} }
} }
// Ignore host permissions for now. URLPatternSet origins;
URLPatternSet empty_set; if (value->HasKey(keys::kOriginsKey)) {
*ptr = new ExtensionPermissionSet(apis, empty_set, empty_set); ListValue* origin_list = NULL;
if (!value->GetList(keys::kOriginsKey, &origin_list)) {
*bad_message = true;
return false;
}
for (size_t i = 0; i < origin_list->GetSize(); ++i) {
std::string pattern;
if (!origin_list->GetString(i, &pattern)) {
*bad_message = true;
return false;
}
URLPattern origin(Extension::kValidHostPermissionSchemes);
URLPattern::ParseResult parse_result =
origin.Parse(pattern, URLPattern::IGNORE_PORTS);
if (URLPattern::PARSE_SUCCESS != parse_result) {
*error = ExtensionErrorUtils::FormatErrorMessage(
keys::kInvalidOrigin,
pattern,
URLPattern::GetParseResultString(parse_result));
return false;
}
origins.AddPattern(origin);
}
}
*ptr = new ExtensionPermissionSet(apis, origins, URLPatternSet());
return true; return true;
} }
...@@ -110,7 +139,7 @@ void ExtensionPermissionsManager::AddPermissions( ...@@ -110,7 +139,7 @@ void ExtensionPermissionsManager::AddPermissions(
// Update the granted permissions so we don't auto-disable the extension. // Update the granted permissions so we don't auto-disable the extension.
extension_service_->GrantPermissions(extension); extension_service_->GrantPermissions(extension);
NotifyPermissionsUpdated(extension, total.get(), added.get(), ADDED); NotifyPermissionsUpdated(ADDED, extension, added.get());
} }
void ExtensionPermissionsManager::RemovePermissions( void ExtensionPermissionsManager::RemovePermissions(
...@@ -127,7 +156,7 @@ void ExtensionPermissionsManager::RemovePermissions( ...@@ -127,7 +156,7 @@ void ExtensionPermissionsManager::RemovePermissions(
// extension to add them again without prompting the user. // extension to add them again without prompting the user.
extension_service_->UpdateActivePermissions(extension, total.get()); extension_service_->UpdateActivePermissions(extension, total.get());
NotifyPermissionsUpdated(extension, total.get(), removed.get(), REMOVED); NotifyPermissionsUpdated(REMOVED, extension, removed.get());
} }
void ExtensionPermissionsManager::DispatchEvent( void ExtensionPermissionsManager::DispatchEvent(
...@@ -146,10 +175,9 @@ void ExtensionPermissionsManager::DispatchEvent( ...@@ -146,10 +175,9 @@ void ExtensionPermissionsManager::DispatchEvent(
} }
void ExtensionPermissionsManager::NotifyPermissionsUpdated( void ExtensionPermissionsManager::NotifyPermissionsUpdated(
EventType event_type,
const Extension* extension, const Extension* extension,
const ExtensionPermissionSet* active, const ExtensionPermissionSet* changed) {
const ExtensionPermissionSet* changed,
EventType event_type) {
if (!changed || changed->IsEmpty()) if (!changed || changed->IsEmpty())
return; return;
...@@ -183,10 +211,11 @@ void ExtensionPermissionsManager::NotifyPermissionsUpdated( ...@@ -183,10 +211,11 @@ void ExtensionPermissionsManager::NotifyPermissionsUpdated(
Profile* profile = Profile::FromBrowserContext(host->browser_context()); Profile* profile = Profile::FromBrowserContext(host->browser_context());
if (extension_service_->profile()->IsSameProfile(profile)) if (extension_service_->profile()->IsSameProfile(profile))
host->Send(new ExtensionMsg_UpdatePermissions( host->Send(new ExtensionMsg_UpdatePermissions(
static_cast<int>(reason),
extension->id(), extension->id(),
active->apis(), changed->apis(),
active->explicit_hosts(), changed->explicit_hosts(),
active->scriptable_hosts())); changed->scriptable_hosts()));
} }
} }
...@@ -235,7 +264,8 @@ bool RemovePermissionsFunction::RunImpl() { ...@@ -235,7 +264,8 @@ bool RemovePermissionsFunction::RunImpl() {
i != apis.end(); ++i) { i != apis.end(); ++i) {
const ExtensionAPIPermission* api = info->GetByID(*i); const ExtensionAPIPermission* api = info->GetByID(*i);
if (!api->supports_optional()) { if (!api->supports_optional()) {
error_ = base::StringPrintf(keys::kNotWhitelistedError, api->name()); error_ = ExtensionErrorUtils::FormatErrorMessage(
keys::kNotWhitelistedError, api->name());
return false; return false;
} }
} }
...@@ -286,7 +316,8 @@ bool RequestPermissionsFunction::RunImpl() { ...@@ -286,7 +316,8 @@ bool RequestPermissionsFunction::RunImpl() {
i != apis.end(); ++i) { i != apis.end(); ++i) {
const ExtensionAPIPermission* api = info->GetByID(*i); const ExtensionAPIPermission* api = info->GetByID(*i);
if (!api->supports_optional()) { if (!api->supports_optional()) {
error_ = base::StringPrintf(keys::kNotWhitelistedError, api->name()); error_ = ExtensionErrorUtils::FormatErrorMessage(
keys::kNotWhitelistedError, api->name());
return false; return false;
} }
} }
......
...@@ -48,16 +48,14 @@ class ExtensionPermissionsManager { ...@@ -48,16 +48,14 @@ class ExtensionPermissionsManager {
const char* event_name, const char* event_name,
const ExtensionPermissionSet* changed_permissions); const ExtensionPermissionSet* changed_permissions);
// Issues the relevant events, messages and notifications when the permissions // Issues the relevant events, messages and notifications when the
// have changed for the |extension| (|changed| is the permission delta, while // |extension|'s permissions have |changed| (|changed| is the delta).
// |active| is the new permission set). Specifically, this sends the // Specifically, this sends the EXTENSION_PERMISSIONS_UPDATED notification,
// EXTENSION_PERMISSIONS_UPDATED notification, the // the ExtensionMsg_UpdatePermissions IPC message, and fires the
// ExtensionMsg_UpdatePermissions IPC message, and fires the onAdded/onRemoved // onAdded/onRemoved events in the extension.
// events in the extension. void NotifyPermissionsUpdated(EventType event_type,
void NotifyPermissionsUpdated(const Extension* extension, const Extension* extension,
const ExtensionPermissionSet* active, const ExtensionPermissionSet* changed);
const ExtensionPermissionSet* changed,
EventType event_type);
ExtensionService* extension_service_; ExtensionService* extension_service_;
}; };
......
...@@ -7,15 +7,18 @@ ...@@ -7,15 +7,18 @@
namespace extension_permissions_module_constants { namespace extension_permissions_module_constants {
const char kApisKey[] = "permissions"; const char kApisKey[] = "permissions";
const char kOriginsKey[] = "origins";
const char kCantRemoveRequiredPermissionsError[] = const char kCantRemoveRequiredPermissionsError[] =
"You cannot remove required permissions."; "You cannot remove required permissions.";
const char kNotInOptionalPermissionsError[] = const char kNotInOptionalPermissionsError[] =
"Optional permissions must be listed in extension manifest."; "Optional permissions must be listed in extension manifest.";
const char kNotWhitelistedError[] = const char kNotWhitelistedError[] =
"The optional permissions API does not support '%s'."; "The optional permissions API does not support '*'.";
const char kUnknownPermissionError[] = const char kUnknownPermissionError[] =
"'%s' is not a recognized permission."; "'*' is not a recognized permission.";
const char kInvalidOrigin[] =
"Invalid value for origin pattern *: *";
const char kOnAdded[] = "experimental.permissions.onAdded"; const char kOnAdded[] = "experimental.permissions.onAdded";
const char kOnRemoved[] = "experimental.permissions.onRemoved"; const char kOnRemoved[] = "experimental.permissions.onRemoved";
......
...@@ -12,12 +12,14 @@ namespace extension_permissions_module_constants { ...@@ -12,12 +12,14 @@ namespace extension_permissions_module_constants {
// Keys used in serializing permissions data and events. // Keys used in serializing permissions data and events.
extern const char kApisKey[]; extern const char kApisKey[];
extern const char kOriginsKey[];
// Error messages. // Error messages.
extern const char kCantRemoveRequiredPermissionsError[]; extern const char kCantRemoveRequiredPermissionsError[];
extern const char kNotInOptionalPermissionsError[]; extern const char kNotInOptionalPermissionsError[];
extern const char kNotWhitelistedError[]; extern const char kNotWhitelistedError[];
extern const char kUnknownPermissionError[]; extern const char kUnknownPermissionError[];
extern const char kInvalidOrigin[];
// Event names. // Event names.
extern const char kOnAdded[]; extern const char kOnAdded[];
......
...@@ -9,6 +9,16 @@ ...@@ -9,6 +9,16 @@
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/extensions/extension_permission_set.h"
#include "net/base/mock_host_resolver.h"
namespace {
static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
int schemes = URLPattern::SCHEME_ALL;
extent->AddPattern(URLPattern(schemes, pattern));
}
} // namespace
class ExperimentalApiTest : public ExtensionApiTest { class ExperimentalApiTest : public ExtensionApiTest {
public: public:
...@@ -60,14 +70,19 @@ IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsGranted) { ...@@ -60,14 +70,19 @@ IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsGranted) {
apis.insert(ExtensionAPIPermission::kTab); apis.insert(ExtensionAPIPermission::kTab);
apis.insert(ExtensionAPIPermission::kManagement); apis.insert(ExtensionAPIPermission::kManagement);
apis.insert(ExtensionAPIPermission::kPermissions); apis.insert(ExtensionAPIPermission::kPermissions);
URLPatternSet explicit_hosts;
AddPattern(&explicit_hosts, "http://a.com/*");
AddPattern(&explicit_hosts, "http://*.c.com/*");
scoped_refptr<ExtensionPermissionSet> granted_permissions = scoped_refptr<ExtensionPermissionSet> granted_permissions =
new ExtensionPermissionSet(apis, URLPatternSet(), URLPatternSet()); new ExtensionPermissionSet(apis, explicit_hosts, URLPatternSet());
ExtensionPrefs* prefs = ExtensionPrefs* prefs =
browser()->profile()->GetExtensionService()->extension_prefs(); browser()->profile()->GetExtensionService()->extension_prefs();
prefs->AddGrantedPermissions("kjmkgkdkpedkejedfhmfcenooemhbpbo", prefs->AddGrantedPermissions("kjmkgkdkpedkejedfhmfcenooemhbpbo",
granted_permissions); granted_permissions);
host_resolver()->AddRule("*.com", "127.0.0.1");
ASSERT_TRUE(StartTestServer());
EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_;
} }
...@@ -76,11 +91,15 @@ IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsAutoConfirm) { ...@@ -76,11 +91,15 @@ IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsAutoConfirm) {
// Rather than setting the granted permissions, set the UI autoconfirm flag // Rather than setting the granted permissions, set the UI autoconfirm flag
// and run the same tests. // and run the same tests.
RequestPermissionsFunction::SetAutoConfirmForTests(true); RequestPermissionsFunction::SetAutoConfirmForTests(true);
host_resolver()->AddRule("*.com", "127.0.0.1");
ASSERT_TRUE(StartTestServer());
EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_; EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_;
} }
// Test that denying the optional permissions confirmation dialog works. // Test that denying the optional permissions confirmation dialog works.
IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsDeny) { IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsDeny) {
RequestPermissionsFunction::SetAutoConfirmForTests(false); RequestPermissionsFunction::SetAutoConfirmForTests(false);
host_resolver()->AddRule("*.com", "127.0.0.1");
ASSERT_TRUE(StartTestServer());
EXPECT_TRUE(RunExtensionTest("permissions/optional_deny")) << message_; EXPECT_TRUE(RunExtensionTest("permissions/optional_deny")) << message_;
} }
...@@ -1071,6 +1071,12 @@ ...@@ -1071,6 +1071,12 @@
"items": {"type": "string"}, "items": {"type": "string"},
"optional": true, "optional": true,
"description": "List of named permissions (does not include hosts or origins)." "description": "List of named permissions (does not include hosts or origins)."
},
"origins": {
"type": "array",
"items": {"type": "string"},
"optional": true,
"description": "List of origin permissions."
} }
} }
} }
......
...@@ -1715,6 +1715,85 @@ ...@@ -1715,6 +1715,85 @@
<div></div> <div></div>
</dd> </dd>
</div>
</div><div>
<div>
<dt>
<var>origins</var>
<em>
<!-- TYPE -->
<div style="display:inline">
(
<span class="optional">optional</span>
<span class="enum" style="display: none; ">enumerated</span>
<span id="typeTemplate">
<span style="display: none; ">
<a> Type</a>
</span>
<span>
<span>
array of <span><span>
<span style="display: none; ">
<a> Type</a>
</span>
<span>
<span style="display: none; ">
array of <span><span></span></span>
</span>
<span>string</span>
<span style="display: none; "></span>
</span>
</span></span>
</span>
<span style="display: none; ">paramType</span>
<span style="display: none; "></span>
</span>
</span>
)
</div>
</em>
</dt>
<dd class="todo" style="display: none; ">
Undocumented.
</dd>
<dd>List of origin permissions.</dd>
<dd style="display: none; ">
This parameter was added in version
<b><span></span></b>.
You must omit this parameter in earlier versions,
and you may omit it in any version. If you require this
parameter, the manifest key
<a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
can ensure that your extension won't be run in an earlier browser version.
</dd>
<!-- OBJECT PROPERTIES -->
<dd style="display: none; ">
<dl>
<div>
<div>
</div>
</div>
</dl>
</dd>
<!-- OBJECT METHODS -->
<dd style="display: none; ">
<div></div>
</dd>
<!-- OBJECT EVENT FIELDS -->
<dd style="display: none; ">
<div></div>
</dd>
<!-- FUNCTION PARAMETERS -->
<dd style="display: none; ">
<div></div>
</dd>
</div> </div>
</div> </div>
</dl> </dl>
......
...@@ -234,11 +234,12 @@ IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId, ...@@ -234,11 +234,12 @@ IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId,
int /* id of browser window */) int /* id of browser window */)
// Tell the renderer to update an extension's permission set. // Tell the renderer to update an extension's permission set.
IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdatePermissions, IPC_MESSAGE_CONTROL5(ExtensionMsg_UpdatePermissions,
int /* UpdateExtensionPermissionsInfo::REASON */,
std::string /* extension_id*/, std::string /* extension_id*/,
ExtensionAPIPermissionSet, ExtensionAPIPermissionSet /* permissions */,
URLPatternSet, URLPatternSet /* explicit_hosts */,
URLPatternSet) URLPatternSet /* scriptable_hosts */)
// Tell the renderer which type this view is. // Tell the renderer which type this view is.
IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType, IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType,
......
...@@ -98,7 +98,7 @@ void ExtensionDispatcher::WebKitInitialized() { ...@@ -98,7 +98,7 @@ void ExtensionDispatcher::WebKitInitialized() {
iter != active_extension_ids_.end(); ++iter) { iter != active_extension_ids_.end(); ++iter) {
const Extension* extension = extensions_.GetByID(*iter); const Extension* extension = extensions_.GetByID(*iter);
if (extension) if (extension)
InitHostPermissions(extension); InitOriginPermissions(extension);
} }
is_webkit_initialized_ = true; is_webkit_initialized_ = true;
...@@ -234,10 +234,13 @@ void ExtensionDispatcher::OnActivateExtension( ...@@ -234,10 +234,13 @@ void ExtensionDispatcher::OnActivateExtension(
return; return;
if (is_webkit_initialized_) if (is_webkit_initialized_)
InitHostPermissions(extension); InitOriginPermissions(extension);
} }
void ExtensionDispatcher::InitHostPermissions(const Extension* extension) { void ExtensionDispatcher::InitOriginPermissions(const Extension* extension) {
// TODO(jstritar): We should try to remove this special case. Also, these
// whitelist entries need to be updated when the kManagement permission
// changes.
if (extension->HasAPIPermission(ExtensionAPIPermission::kManagement)) { if (extension->HasAPIPermission(ExtensionAPIPermission::kManagement)) {
WebSecurityPolicy::addOriginAccessWhitelistEntry( WebSecurityPolicy::addOriginAccessWhitelistEntry(
extension->url(), extension->url(),
...@@ -246,10 +249,17 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) { ...@@ -246,10 +249,17 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) {
false); false);
} }
const URLPatternSet& permissions = UpdateOriginPermissions(UpdatedExtensionPermissionsInfo::ADDED,
extension->GetActivePermissions()->explicit_hosts(); extension,
for (URLPatternSet::const_iterator i = permissions.begin(); extension->GetActivePermissions()->explicit_hosts());
i != permissions.end(); ++i) { }
void ExtensionDispatcher::UpdateOriginPermissions(
UpdatedExtensionPermissionsInfo::Reason reason,
const Extension* extension,
const URLPatternSet& origins) {
for (URLPatternSet::const_iterator i = origins.begin();
i != origins.end(); ++i) {
const char* schemes[] = { const char* schemes[] = {
chrome::kHttpScheme, chrome::kHttpScheme,
chrome::kHttpsScheme, chrome::kHttpsScheme,
...@@ -258,17 +268,20 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) { ...@@ -258,17 +268,20 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) {
}; };
for (size_t j = 0; j < arraysize(schemes); ++j) { for (size_t j = 0; j < arraysize(schemes); ++j) {
if (i->MatchesScheme(schemes[j])) { if (i->MatchesScheme(schemes[j])) {
WebSecurityPolicy::addOriginAccessWhitelistEntry( ((reason == UpdatedExtensionPermissionsInfo::REMOVED) ?
extension->url(), WebSecurityPolicy::removeOriginAccessWhitelistEntry :
WebString::fromUTF8(schemes[j]), WebSecurityPolicy::addOriginAccessWhitelistEntry)(
WebString::fromUTF8(i->host()), extension->url(),
i->match_subdomains()); WebString::fromUTF8(schemes[j]),
WebString::fromUTF8(i->host()),
i->match_subdomains());
} }
} }
} }
} }
void ExtensionDispatcher::OnUpdatePermissions( void ExtensionDispatcher::OnUpdatePermissions(
int reason_id,
const std::string& extension_id, const std::string& extension_id,
const ExtensionAPIPermissionSet& apis, const ExtensionAPIPermissionSet& apis,
const URLPatternSet& explicit_hosts, const URLPatternSet& explicit_hosts,
...@@ -277,8 +290,23 @@ void ExtensionDispatcher::OnUpdatePermissions( ...@@ -277,8 +290,23 @@ void ExtensionDispatcher::OnUpdatePermissions(
if (!extension) if (!extension)
return; return;
extension->SetActivePermissions( scoped_refptr<const ExtensionPermissionSet> delta =
new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts)); new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
scoped_refptr<const ExtensionPermissionSet> old_active =
extension->GetActivePermissions();
UpdatedExtensionPermissionsInfo::Reason reason =
static_cast<UpdatedExtensionPermissionsInfo::Reason>(reason_id);
const ExtensionPermissionSet* new_active = NULL;
if (reason == UpdatedExtensionPermissionsInfo::ADDED) {
new_active = ExtensionPermissionSet::CreateUnion(old_active, delta);
} else {
CHECK_EQ(UpdatedExtensionPermissionsInfo::REMOVED, reason);
new_active = ExtensionPermissionSet::CreateDifference(old_active, delta);
}
extension->SetActivePermissions(new_active);
UpdateOriginPermissions(reason, extension, explicit_hosts);
} }
void ExtensionDispatcher::OnUpdateUserScripts( void ExtensionDispatcher::OnUpdateUserScripts(
......
...@@ -78,7 +78,8 @@ class ExtensionDispatcher : public RenderProcessObserver { ...@@ -78,7 +78,8 @@ class ExtensionDispatcher : public RenderProcessObserver {
const std::vector<std::string>& page_actions); const std::vector<std::string>& page_actions);
void OnActivateApplication(const std::string& extension_id); void OnActivateApplication(const std::string& extension_id);
void OnActivateExtension(const std::string& extension_id); void OnActivateExtension(const std::string& extension_id);
void OnUpdatePermissions(const std::string& extension_id, void OnUpdatePermissions(int reason_id,
const std::string& extension_id,
const ExtensionAPIPermissionSet& apis, const ExtensionAPIPermissionSet& apis,
const URLPatternSet& explicit_hosts, const URLPatternSet& explicit_hosts,
const URLPatternSet& scriptable_hosts); const URLPatternSet& scriptable_hosts);
...@@ -92,7 +93,10 @@ class ExtensionDispatcher : public RenderProcessObserver { ...@@ -92,7 +93,10 @@ class ExtensionDispatcher : public RenderProcessObserver {
void RegisterExtension(v8::Extension* extension, bool restrict_to_extensions); void RegisterExtension(v8::Extension* extension, bool restrict_to_extensions);
// Sets up the host permissions for |extension|. // Sets up the host permissions for |extension|.
void InitHostPermissions(const Extension* extension); void InitOriginPermissions(const Extension* extension);
void UpdateOriginPermissions(UpdatedExtensionPermissionsInfo::Reason reason,
const Extension* extension,
const URLPatternSet& origins);
// True if this renderer is running extensions. // True if this renderer is running extensions.
bool is_extension_process_; bool is_extension_process_;
......
<script> <script>
var assertEq = chrome.test.assertEq;
var assertFalse = chrome.test.assertFalse; var assertFalse = chrome.test.assertFalse;
var assertTrue = chrome.test.assertTrue; var assertTrue = chrome.test.assertTrue;
var fail = chrome.test.callbackFail; var fail = chrome.test.callbackFail;
...@@ -21,14 +22,21 @@ var NOT_WHITE_LISTED_ERROR = ...@@ -21,14 +22,21 @@ var NOT_WHITE_LISTED_ERROR =
var UNKNOWN_PERMISSIONS_ERROR = var UNKNOWN_PERMISSIONS_ERROR =
"'*' is not a recognized permission."; "'*' is not a recognized permission.";
var emptyPermissions = {permissions: []}; var emptyPermissions = {permissions: [], origins: []};
var initialPermissions = { var initialPermissions = {
permissions: ['experimental', 'management', 'permissions'] permissions: ['experimental', 'management', 'permissions'],
origins: ['http://a.com/*']
}; };
var permissionsWithTabs = { var permissionsWithTabs = {
permissions: ['experimental', 'management', 'permissions', 'tabs'] permissions: ['experimental', 'management', 'permissions', 'tabs'],
origins: ['http://a.com/*']
}
var permissionsWithOrigin = {
permissions: ['experimental', 'management', 'permissions'],
origins: ['http://a.com/*', 'http://*.c.com/*']
} }
function checkEqualSets(set1, set2) { function checkEqualSets(set1, set2) {
...@@ -44,145 +52,232 @@ function checkEqualSets(set1, set2) { ...@@ -44,145 +52,232 @@ function checkEqualSets(set1, set2) {
} }
function checkPermSetsEq(set1, set2) { function checkPermSetsEq(set1, set2) {
return checkEqualSets(set1.permissions, set2.permissions); return checkEqualSets(set1.permissions, set2.permissions) &&
checkEqualSets(set1.origins, set2.origins);
} }
chrome.test.runTests([ chrome.test.getConfig(function(config) {
function contains() {
chrome.experimental.permissions.contains( function doReq(domain, callback) {
{permissions: ['management']}, var req = new XMLHttpRequest();
pass(function(result) { var url = domain + ":PORT/files/extensions/test_file.txt";
assertTrue(result); url = url.replace(/PORT/, config.testServer.port);
}));
chrome.experimental.permissions.contains( chrome.test.log("Requesting url: " + url);
{permissions: ['devtools']}, req.open("GET", url, true);
pass(function(result) {
assertFalse(result); req.onload = function() {
})); assertEq(200, req.status);
chrome.experimental.permissions.contains( assertEq("Hello!", req.responseText);
{permissions: ['permissions', 'management']}, callback(true);
pass(function(result) { };
assertTrue(result);
})); req.onerror = function() {
chrome.experimental.permissions.contains( chrome.test.log("status: " + req.status);
{permissions: ['management', 'permissions']}, chrome.test.log("text: " + req.responseText);
pass(function(result) { callback(false);
assertTrue(result); };
}));
}, req.send(null);
}
function getAll() {
chrome.experimental.permissions.getAll(pass(function(permissions) { chrome.test.runTests([
assertTrue(checkPermSetsEq(initialPermissions, permissions)); function contains() {
})); chrome.experimental.permissions.contains(
}, {permissions: ['management'], origins: ['http://a.com/*']},
pass(function(result) { assertTrue(result); }));
// Nothing should happen if we request permission we already have chrome.experimental.permissions.contains(
function requestNoOp() { {permissions: ['devtools'], origins: ['http://a.com/*']},
chrome.experimental.permissions.request( pass(function(result) { assertFalse(result); }));
{permissions:['management']}, chrome.experimental.permissions.contains(
pass(function(granted) { {permissions: ['permissions', 'management']},
pass(function(result) { assertTrue(result); }));
chrome.experimental.permissions.contains(
{permissions: ['management', 'permissions']},
pass(function(result) { assertTrue(result); }));
},
function getAll() {
chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(initialPermissions, permissions));
}));
},
// Nothing should happen if we request permission we already have
function requestNoOp() {
chrome.experimental.permissions.request(
{permissions:['management'], origins:['http://a.com/*']},
pass(function(granted) { assertTrue(granted); }));
},
// We should get an error when requesting permissions that haven't been
// defined in "optional_permissions".
function requestNonOptional() {
chrome.experimental.permissions.request(
{permissions: ['debugger']}, fail(NOT_OPTIONAL_ERROR));
chrome.experimental.permissions.request(
{origins: ['http://*.b.com/*']}, fail(NOT_OPTIONAL_ERROR));
chrome.experimental.permissions.request(
{permissions: ['tabs'], origins: ['http://*.b.com/*']},
fail(NOT_OPTIONAL_ERROR));
},
// We should be able to request the tabs API since it's in the granted
// permissions list (see permissions_apitest.cc).
function requestTabs() {
try {
chrome.windows.getAll({populate: true}, function() {
chrome.test.fail("Should not have tabs API permission.");
});
} catch (e) {
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
}
listenOnce(chrome.experimental.permissions.onAdded,
function(permissions) {
assertTrue(permissions.permissions.length == 1);
assertTrue(permissions.permissions[0] == 'tabs');
});
chrome.experimental.permissions.request(
{permissions:['tabs']},
pass(function(granted) {
assertTrue(granted);
chrome.windows.getAll({populate: true}, pass(function(windows) {
assertTrue(true);
}));
chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(permissionsWithTabs, permissions));
}));
}));
},
// You can't remove required permissions.
function removeRequired() {
chrome.experimental.permissions.remove(
{permissions: ['management']}, fail(REQUIRED_ERROR));
chrome.experimental.permissions.remove(
{origins: ['http://a.com/*']}, fail(REQUIRED_ERROR));
chrome.experimental.permissions.remove(
{permissions: ['tabs'], origins: ['http://a.com/*']},
fail(REQUIRED_ERROR));
},
// You can remove permissions you don't have (nothing happens).
function removeNoOp() {
chrome.experimental.permissions.remove(
{permissions:['background']},
pass(function(removed) { assertTrue(removed); }));
chrome.experimental.permissions.remove(
{origins:['http://*.c.com/*']},
pass(function(removed) { assertTrue(removed); }));
chrome.experimental.permissions.remove(
{permissions:['background'], origins:['http://*.c.com/*']},
pass(function(removed) { assertTrue(removed); }));
},
function removeTabs() {
chrome.windows.getAll({populate: true}, pass(function(windows) {
assertTrue(true);
}));
listenOnce(chrome.experimental.permissions.onRemoved,
function(permissions) {
assertTrue(permissions.permissions.length == 1);
assertTrue(permissions.permissions[0] == 'tabs');
});
chrome.experimental.permissions.remove(
{permissions:['tabs']},
pass(function() {
chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(initialPermissions, permissions));
}));
try {
chrome.windows.getAll({populate: true}, function() {
chrome.test.fail("Should not have tabs API permission.");
});
} catch (e) {
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
}
}));
},
// The user shouldn't have to approve permissions that have no warnings.
function noPromptForNoWarnings() {
chrome.experimental.permissions.request(
{permissions: ['notifications']},
pass(function(granted) {
assertTrue(granted); assertTrue(granted);
}));
}, // Remove the notifications permission to return to normal.
chrome.experimental.permissions.remove(
// We should get an error when requesting permissions that haven't been {permissions: ['notifications']},
// defined in "optional_permissions". pass(function(removed) { assertTrue(removed); }));
function requestNonOptional() { }));
chrome.experimental.permissions.request( },
{permissions:['debugger']}, fail(NOT_OPTIONAL_ERROR));
}, // Make sure you can only access the white listed permissions.
function whitelist() {
// We should be able to request the tabs API since it's in the granted var error_msg = NOT_WHITE_LISTED_ERROR.replace('*', 'chromeAuthPrivate');
// permissions list (see permissions_apitest.cc). chrome.experimental.permissions.request(
function requestTabs() { {permissions: ['chromeAuthPrivate']}, fail(error_msg));
try { chrome.experimental.permissions.remove(
chrome.windows.getAll({populate: true}, function() { {permissions: ['chromeAuthPrivate']}, fail(error_msg));
chrome.test.fail("Should not have tabs API permission."); },
function unknownPermission() {
var error_msg = UNKNOWN_PERMISSIONS_ERROR.replace('*', 'asdf');
chrome.experimental.permissions.request(
{permissions: ['asdf']}, fail(error_msg));
},
function requestOrigin() {
doReq('http://c.com', pass(function(success) { assertFalse(success); }));
chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(initialPermissions, permissions));
}));
listenOnce(chrome.experimental.permissions.onAdded,
function(permissions) {
assertTrue(permissions.permissions.length == 0);
assertTrue(permissions.origins.length == 1);
assertTrue(permissions.origins[0] == 'http://*.c.com/*');
}); });
} catch (e) { chrome.experimental.permissions.request(
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0); {origins: ['http://*.c.com/*']},
} pass(function(granted) {
listenOnce(chrome.experimental.permissions.onAdded, function(permissions) {
assertTrue(permissions.permissions.length == 1);
assertTrue(permissions.permissions[0] == 'tabs');
});
chrome.experimental.permissions.request(
{permissions:['tabs']},
pass(function(granted) {
assertTrue(granted); assertTrue(granted);
chrome.windows.getAll({populate: true}, pass(function(windows) {
assertTrue(true);
}));
chrome.experimental.permissions.getAll(pass(function(permissions) { chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(permissionsWithTabs, permissions)); assertTrue(checkPermSetsEq(permissionsWithOrigin, permissions));
})); }));
})); chrome.experimental.permissions.contains(
}, {origins:['http://*.c.com/*']},
pass(function(result) { assertTrue(result); }));
// You can't remove required permissions. doReq('http://c.com', pass(function(result) { assertTrue(result); }));
function removeRequired() { }));
chrome.experimental.permissions.remove( },
{permissions:['management']}, fail(REQUIRED_ERROR));
}, function removeOrigin() {
doReq('http://c.com', pass(function(result) { assertTrue(result); }));
// You can remove permissions you don't have (nothing happens).
function removeNoOp() { listenOnce(chrome.experimental.permissions.onRemoved,
chrome.experimental.permissions.remove( function(permissions) {
{permissions:['background']}, assertTrue(permissions.permissions.length == 0);
pass(function(removed) { assertTrue(permissions.origins.length == 1);
assertTrue(permissions.origins[0] == 'http://*.c.com/*');
});
chrome.experimental.permissions.remove(
{origins: ['http://*.c.com/*']},
pass(function(removed) {
assertTrue(removed); assertTrue(removed);
}));
},
function removeTabs() {
chrome.windows.getAll({populate: true}, pass(function(windows) {
assertTrue(true);
}));
listenOnce(chrome.experimental.permissions.onRemoved,
function(permissions) {
assertTrue(permissions.permissions.length == 1);
assertTrue(permissions.permissions[0] == 'tabs');
});
chrome.experimental.permissions.remove(
{permissions:['tabs']},
pass(function() {
chrome.experimental.permissions.getAll(pass(function(permissions) { chrome.experimental.permissions.getAll(pass(function(permissions) {
assertTrue(checkPermSetsEq(initialPermissions, permissions)); assertTrue(checkPermSetsEq(initialPermissions, permissions));
})); }));
try { chrome.experimental.permissions.contains(
chrome.windows.getAll({populate: true}, function() { {origins:['http://*.c.com/*']},
chrome.test.fail("Should not have tabs API permission."); pass(function(result) { assertFalse(result); }));
}); doReq('http://c.com', pass(function(result) { assertFalse(result); }));
} catch (e) { }));
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0); }
}
})); ]);
}, });
// The user shouldn't have to approve permissions that have no warnings.
// TODO(jstritar): move to its own test and set a low timeout
function noPromptForNoWarnings() {
chrome.experimental.permissions.request(
{permissions: ['notifications']},
pass(function(granted) {
assertTrue(granted);
}));
},
// Make sure you can only access the white listed permissions.
function whitelist() {
var error_msg = NOT_WHITE_LISTED_ERROR.replace('*', 'chromeAuthPrivate');
chrome.experimental.permissions.request(
{permissions: ['chromeAuthPrivate']}, fail(error_msg));
chrome.experimental.permissions.remove(
{permissions: ['chromeAuthPrivate']}, fail(error_msg));
},
function unknownPermission() {
var error_msg = UNKNOWN_PERMISSIONS_ERROR.replace('*', 'asdf');
chrome.experimental.permissions.request(
{permissions: ['asdf']}, fail(error_msg));
}
]);
</script> </script>
...@@ -8,12 +8,14 @@ ...@@ -8,12 +8,14 @@
"experimental", "experimental",
"management", "management",
"permissions", "permissions",
"http://example.org/" "http://a.com/*"
], ],
"optional_permissions": [ "optional_permissions": [
"tabs", "tabs",
"management", "management",
"notifications", "notifications",
"background" "background",
"http://a.com/*",
"http://*.c.com/*"
] ]
} }
...@@ -7,30 +7,58 @@ var pass = chrome.test.callbackPass; ...@@ -7,30 +7,58 @@ var pass = chrome.test.callbackPass;
var NO_TABS_PERMISSION = var NO_TABS_PERMISSION =
"You do not have permission to use 'windows.getAll'."; "You do not have permission to use 'windows.getAll'.";
chrome.test.runTests([ chrome.test.getConfig(function(config) {
function denyRequest() {
chrome.experimental.permissions.request( function doReq(domain, callback) {
{permissions: ['tabs']}, var req = new XMLHttpRequest();
pass(function(granted) { var url = domain + ":PORT/files/extensions/test_file.txt";
// They were not granted, and there should be no error. url = url.replace(/PORT/, config.testServer.port);
assertFalse(granted);
assertTrue(chrome.extension.lastError === undefined); chrome.test.log("Requesting url: " + url);
req.open("GET", url, true);
// Make sure they weren't granted...
chrome.experimental.permissions.contains( req.onload = function() {
{permissions: ['tabs']}, assertEq(200, req.status);
pass(function(result) { assertEq("Hello!", req.responseText);
assertFalse(result); callback(true);
})); };
try { req.onerror = function() {
chrome.windows.getAll({populate: true}, function() { chrome.test.log("status: " + req.status);
chrome.test.fail("Should not have tabs API permission."); chrome.test.log("text: " + req.responseText);
}); callback(false);
} catch (e) { };
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
} req.send(null);
}));
} }
]);
chrome.test.runTests([
function denyRequest() {
chrome.experimental.permissions.request(
{permissions: ['tabs'], origins: ['http://*.c.com/*']},
pass(function(granted) {
// They were not granted, and there should be no error.
assertFalse(granted);
assertTrue(chrome.extension.lastError === undefined);
// Make sure they weren't granted...
chrome.experimental.permissions.contains(
{permissions: ['tabs'], origins:['http://*.c.com/*']},
pass(function(result) { assertFalse(result); }));
try {
chrome.windows.getAll({populate: true}, function() {
chrome.test.fail("Should not have tabs API permission.");
});
} catch (e) {
assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
}
doReq('http://b.c.com/', pass(function(result) {
assertFalse(result);
}));
}));
}
]);
});
</script> </script>
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
"description": "permissions/optional_deny", "description": "permissions/optional_deny",
"version": "0.1", "version": "0.1",
"background_page": "background.html", "background_page": "background.html",
"permissions": ["experimental", "permissions", "http://example.org/"], "permissions": ["experimental", "permissions", "http://a.com/*"],
"optional_permissions": ["tabs", "management"] "optional_permissions": ["tabs", "management", "http://*.c.com/*"]
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment