| // Copyright 2016 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/browser/extensions/api/permissions/permissions_api.h" |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/extensions/extension_api_unittest.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_service_test_with_install.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/extensions/permissions_test_util.h" |
| #include "chrome/browser/extensions/permissions_updater.h" |
| #include "chrome/browser/extensions/scripting_permissions_modifier.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/crx_file/id_util.h" |
| #include "extensions/common/extension_builder.h" |
| #include "extensions/common/extension_features.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| using permissions_test_util::GetPatternsAsStrings; |
| |
| scoped_refptr<const Extension> CreateExtensionWithPermissions( |
| std::unique_ptr<base::Value> permissions, |
| const std::string& name, |
| bool allow_file_access) { |
| int creation_flags = Extension::NO_FLAGS; |
| if (allow_file_access) |
| creation_flags |= Extension::ALLOW_FILE_ACCESS; |
| return ExtensionBuilder() |
| .SetLocation(Manifest::INTERNAL) |
| .SetManifest(DictionaryBuilder() |
| .Set("name", name) |
| .Set("description", "foo") |
| .Set("manifest_version", 2) |
| .Set("version", "0.1.2.3") |
| .Set("permissions", std::move(permissions)) |
| .Build()) |
| .AddFlags(creation_flags) |
| .SetID(crx_file::id_util::GenerateId(name)) |
| .Build(); |
| } |
| |
| // Helper function to create a base::Value from a list of strings. |
| std::unique_ptr<base::Value> StringVectorToValue( |
| const std::vector<std::string>& strings) { |
| ListBuilder builder; |
| for (const auto& str : strings) |
| builder.Append(str); |
| return builder.Build(); |
| } |
| |
| // Runs permissions.request() with the provided |args|, and returns the result |
| // of the API call. Expects the function to succeed. |
| // Populates |did_prompt_user| with whether the user would be prompted for the |
| // new permissions. |
| bool RunRequestFunction( |
| const Extension& extension, |
| Browser* browser, |
| const char* args, |
| std::unique_ptr<const PermissionSet>* prompted_permissions_out) { |
| auto function = base::MakeRefCounted<PermissionsRequestFunction>(); |
| function->set_user_gesture(true); |
| function->set_extension(&extension); |
| std::unique_ptr<base::Value> result( |
| extension_function_test_utils::RunFunctionAndReturnSingleResult( |
| function.get(), args, browser, api_test_utils::NONE)); |
| if (!function->GetError().empty()) { |
| ADD_FAILURE() << "Unexpected function error: " << function->GetError(); |
| return false; |
| } |
| |
| if (!result || !result->is_bool()) { |
| ADD_FAILURE() << "Unexpected function result."; |
| return false; |
| } |
| |
| *prompted_permissions_out = function->TakePromptedPermissionsForTesting(); |
| |
| return result->GetBool(); |
| } |
| |
| } // namespace |
| |
| class PermissionsAPIUnitTest : public ExtensionServiceTestWithInstall { |
| public: |
| PermissionsAPIUnitTest() {} |
| ~PermissionsAPIUnitTest() override {} |
| Browser* browser() { return browser_.get(); } |
| |
| // Runs chrome.permissions.contains(|json_query|). |
| bool RunContainsFunction(const std::string& manifest_permission, |
| const std::string& args_string, |
| bool allow_file_access) { |
| SCOPED_TRACE(args_string); |
| ListBuilder required_permissions; |
| required_permissions.Append(manifest_permission); |
| scoped_refptr<const Extension> extension = CreateExtensionWithPermissions( |
| required_permissions.Build(), "My Extension", allow_file_access); |
| scoped_refptr<PermissionsContainsFunction> function( |
| new PermissionsContainsFunction()); |
| function->set_extension(extension.get()); |
| bool run_result = extension_function_test_utils::RunFunction( |
| function.get(), args_string, browser(), api_test_utils::NONE); |
| EXPECT_TRUE(run_result) << function->GetError(); |
| |
| bool has_permission; |
| EXPECT_TRUE(function->GetResultList()->GetBoolean(0u, &has_permission)); |
| return has_permission; |
| } |
| |
| // Adds the extension to the ExtensionService, and grants any inital |
| // permissions. |
| void AddExtensionAndGrantPermissions(const Extension& extension) { |
| PermissionsUpdater updater(profile()); |
| updater.InitializePermissions(&extension); |
| updater.GrantActivePermissions(&extension); |
| service()->AddExtension(&extension); |
| } |
| |
| private: |
| // ExtensionServiceTestBase: |
| void SetUp() override { |
| ExtensionServiceTestWithInstall::SetUp(); |
| PermissionsRequestFunction::SetAutoConfirmForTests(true); |
| InitializeEmptyExtensionService(); |
| browser_window_.reset(new TestBrowserWindow()); |
| Browser::CreateParams params(profile(), true); |
| params.type = Browser::TYPE_TABBED; |
| params.window = browser_window_.get(); |
| browser_.reset(new Browser(params)); |
| } |
| // ExtensionServiceTestBase: |
| void TearDown() override { |
| browser_.reset(); |
| browser_window_.reset(); |
| PermissionsRequestFunction::ResetAutoConfirmForTests(); |
| ExtensionServiceTestWithInstall::TearDown(); |
| } |
| |
| std::unique_ptr<TestBrowserWindow> browser_window_; |
| std::unique_ptr<Browser> browser_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PermissionsAPIUnitTest); |
| }; |
| |
| TEST_F(PermissionsAPIUnitTest, Contains) { |
| // 1. Since the extension does not have file:// origin access, expect it |
| // to return false; |
| bool expected_has_permission = false; |
| bool has_permission = RunContainsFunction( |
| "tabs", "[{\"origins\":[\"file://*\"]}]", false /* allow_file_access */); |
| EXPECT_EQ(expected_has_permission, has_permission); |
| |
| // 2. Extension has tabs permission, expect to return true. |
| expected_has_permission = true; |
| has_permission = RunContainsFunction("tabs", "[{\"permissions\":[\"tabs\"]}]", |
| false /* allow_file_access */); |
| EXPECT_EQ(expected_has_permission, has_permission); |
| |
| // 3. Extension has file permission, but not active. Expect to return false. |
| expected_has_permission = false; |
| has_permission = |
| RunContainsFunction("file://*", "[{\"origins\":[\"file://*\"]}]", |
| false /* allow_file_access */); |
| EXPECT_EQ(expected_has_permission, has_permission); |
| |
| // 4. Same as #3, but this time with file access allowed. |
| expected_has_permission = true; |
| has_permission = |
| RunContainsFunction("file:///*", "[{\"origins\":[\"file:///*\"]}]", |
| true /* allow_file_access */); |
| EXPECT_EQ(expected_has_permission, has_permission); |
| } |
| |
| TEST_F(PermissionsAPIUnitTest, ContainsAndGetAllWithRuntimeHostPermissions) { |
| // This test relies on the click-to-script feature. |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| constexpr char kExampleCom[] = "https://example.com/*"; |
| constexpr char kContentScriptCom[] = "https://contentscript.com/*"; |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermission(kExampleCom) |
| .AddContentScript("foo.js", {kContentScriptCom, kExampleCom}) |
| .Build(); |
| |
| AddExtensionAndGrantPermissions(*extension); |
| PermissionsUpdater updater(profile()); |
| updater.InitializePermissions(extension.get()); |
| updater.GrantActivePermissions(extension.get()); |
| service()->AddExtension(extension.get()); |
| |
| auto contains_origin = [this, &extension](const char* origin) { |
| SCOPED_TRACE(origin); |
| auto function = base::MakeRefCounted<PermissionsContainsFunction>(); |
| function->set_extension(extension.get()); |
| if (!extension_function_test_utils::RunFunction( |
| function.get(), |
| base::StringPrintf(R"([{"origins": ["%s"]}])", origin), browser(), |
| api_test_utils::NONE)) { |
| ADD_FAILURE() << "Running function failed: " << function->GetError(); |
| } |
| |
| return function->GetResultList()->GetList()[0].GetBool(); |
| }; |
| |
| auto get_all = [this, &extension]() { |
| auto function = base::MakeRefCounted<PermissionsGetAllFunction>(); |
| function->set_extension(extension.get()); |
| |
| std::vector<std::string> origins; |
| if (!extension_function_test_utils::RunFunction( |
| function.get(), "[]", browser(), api_test_utils::NONE)) { |
| ADD_FAILURE() << "Running function failed: " << function->GetError(); |
| return origins; |
| } |
| |
| const base::Value* results = function->GetResultList(); |
| if (results->GetList().size() != 1u || !results->GetList()[0].is_dict()) { |
| ADD_FAILURE() << "Invalid result value"; |
| return origins; |
| } |
| |
| const base::Value* origins_value = |
| results->GetList()[0].FindKeyOfType("origins", base::Value::Type::LIST); |
| for (const auto& value : origins_value->GetList()) |
| origins.push_back(value.GetString()); |
| |
| return origins; |
| }; |
| |
| // Currently, the extension should have access to example.com and |
| // contentscript.com (since permissions are not withheld). |
| EXPECT_TRUE(contains_origin(kExampleCom)); |
| EXPECT_TRUE(contains_origin(kContentScriptCom)); |
| EXPECT_THAT(get_all(), |
| testing::UnorderedElementsAre(kExampleCom, kContentScriptCom)); |
| |
| ScriptingPermissionsModifier modifier(profile(), extension); |
| modifier.SetWithholdHostPermissions(true); |
| |
| // Once we withhold the permission, the contains function should correctly |
| // report the value. |
| EXPECT_FALSE(contains_origin(kExampleCom)); |
| EXPECT_FALSE(contains_origin(kContentScriptCom)); |
| EXPECT_THAT(get_all(), testing::IsEmpty()); |
| |
| constexpr char kChromiumOrg[] = "https://chromium.org/"; |
| modifier.GrantHostPermission(GURL(kChromiumOrg)); |
| |
| // The permissions API only reports active permissions, rather than granted |
| // permissions. This means it will not report values for permissions that |
| // aren't requested. This is probably good, because the extension wouldn't be |
| // able to use them anyway (since they aren't active). |
| EXPECT_FALSE(contains_origin(kChromiumOrg)); |
| EXPECT_THAT(get_all(), testing::IsEmpty()); |
| |
| // Fun edge case: example.com is requested as both a scriptable and an |
| // explicit host. It is technically possible that it may be granted *only* as |
| // one of the two (e.g., only explicit granted). |
| { |
| URLPatternSet explicit_hosts( |
| {URLPattern(Extension::kValidHostPermissionSchemes, kExampleCom)}); |
| permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion( |
| profile(), *extension, |
| PermissionSet(APIPermissionSet(), ManifestPermissionSet(), |
| explicit_hosts, URLPatternSet())); |
| const GURL example_url("https://example.com"); |
| const PermissionSet& active_permissions = |
| extension->permissions_data()->active_permissions(); |
| EXPECT_TRUE(active_permissions.explicit_hosts().MatchesURL(example_url)); |
| EXPECT_FALSE(active_permissions.scriptable_hosts().MatchesURL(example_url)); |
| } |
| // In this case, contains() should return *false* (because not all the |
| // permissions are active, but getAll() should include example.com (because it |
| // has been [partially] granted). In practice, this case should be |
| // exceptionally rare, and we're mostly just making sure that there's some |
| // sane behavior. |
| EXPECT_FALSE(contains_origin(kExampleCom)); |
| EXPECT_THAT(get_all(), testing::ElementsAre(kExampleCom)); |
| } |
| |
| // Tests requesting withheld permissions with the permissions.request() API. |
| TEST_F(PermissionsAPIUnitTest, RequestingWithheldPermissions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| // Create an extension with required host permissions, and withhold those |
| // permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermissions({"https://example.com/*", "https://google.com/*"}) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier(profile(), extension) |
| .SetWithholdHostPermissions(true); |
| |
| const GURL kExampleCom("https://example.com"); |
| const GURL kGoogleCom("https://google.com"); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| |
| // Request one of the withheld permissions. |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE(RunRequestFunction(*extension, browser(), |
| R"([{"origins": ["https://example.com/*"]}])", |
| &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre("https://example.com/*")); |
| |
| // The withheld permission should be granted. |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kExampleCom)); |
| EXPECT_FALSE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kGoogleCom)); |
| } |
| |
| // Tests requesting withheld content script permissions with the |
| // permissions.request() API. |
| TEST_F(PermissionsAPIUnitTest, RequestingWithheldContentScriptPermissions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| constexpr char kContentScriptPattern[] = "https://contentscript.com/*"; |
| // Create an extension with required host permissions, and withhold those |
| // permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddContentScript("foo.js", {kContentScriptPattern}) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier(profile(), extension) |
| .SetWithholdHostPermissions(true); |
| |
| const GURL kContentScriptCom("https://contentscript.com"); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| |
| // Request one of the withheld permissions. |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE( |
| RunRequestFunction(*extension, browser(), |
| R"([{"origins": ["https://contentscript.com/*"]}])", |
| &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre(kContentScriptPattern)); |
| |
| // The withheld permission should be granted. |
| EXPECT_THAT(GetPatternsAsStrings( |
| permissions_data->active_permissions().effective_hosts()), |
| testing::UnorderedElementsAre(kContentScriptPattern)); |
| EXPECT_TRUE( |
| permissions_data->withheld_permissions().effective_hosts().is_empty()); |
| } |
| |
| // Tests requesting a withheld host permission that is both an explicit and a |
| // scriptable host with the permissions.request() API. |
| TEST_F(PermissionsAPIUnitTest, |
| RequestingWithheldExplicitAndScriptablePermissionsInTheSameCall) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| constexpr char kContentScriptPattern[] = "https://example.com/*"; |
| // Create an extension with required host permissions, and withhold those |
| // permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermission("https://example.com/*") |
| .AddContentScript("foo.js", {kContentScriptPattern}) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier(profile(), extension) |
| .SetWithholdHostPermissions(true); |
| |
| const GURL kExampleCom("https://example.com"); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| |
| // Request one of the withheld permissions. |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE(RunRequestFunction(*extension, browser(), |
| R"([{"origins": ["https://example.com/*"]}])", |
| &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre(kContentScriptPattern)); |
| |
| // The withheld permission should be granted to both explicit and scriptable |
| // hosts. |
| EXPECT_TRUE( |
| permissions_data->active_permissions().explicit_hosts().MatchesURL( |
| kExampleCom)); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().scriptable_hosts().MatchesURL( |
| kExampleCom)); |
| } |
| |
| // Tests an extension re-requesting an optional host after the user removes it. |
| TEST_F(PermissionsAPIUnitTest, ReRequestingWithheldOptionalPermissions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| // Create an extension an optional host permissions, and withhold those |
| // permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .SetManifestKey("optional_permissions", |
| StringVectorToValue({"https://chromium.org/*"})) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| |
| const GURL kChromiumOrg("https://chromium.org"); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| { |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE(RunRequestFunction( |
| *extension, browser(), |
| R"([{"origins": ["https://chromium.org/*"]}])", &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre("https://chromium.org/*")); |
| } |
| |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kChromiumOrg)); |
| |
| { |
| URLPattern chromium_org_pattern(Extension::kValidHostPermissionSchemes, |
| "https://chromium.org/*"); |
| PermissionSet permissions(APIPermissionSet(), ManifestPermissionSet(), |
| URLPatternSet({chromium_org_pattern}), |
| URLPatternSet()); |
| permissions_test_util::RevokeRuntimePermissionsAndWaitForCompletion( |
| profile(), *extension, permissions); |
| } |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| |
| PermissionsRequestFunction::SetAutoConfirmForTests(false); |
| { |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_FALSE(RunRequestFunction( |
| *extension, browser(), |
| R"([{"origins": ["https://chromium.org/*"]}])", &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre("https://chromium.org/*")); |
| } |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| } |
| |
| // Tests requesting both optional and withheld permissions in the same call to |
| // permissions.request(). |
| TEST_F(PermissionsAPIUnitTest, RequestingWithheldAndOptionalPermissions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| // Create an extension with required and optional host permissions, and |
| // withhold the required permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermissions({"https://example.com/*", "https://google.com/*"}) |
| .SetManifestKey("optional_permissions", |
| StringVectorToValue({"https://chromium.org/*"})) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier(profile(), extension) |
| .SetWithholdHostPermissions(true); |
| |
| const GURL kExampleCom("https://example.com"); |
| const GURL kGoogleCom("https://google.com"); |
| const GURL kChromiumOrg("https://chromium.org"); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().is_empty()); |
| |
| // Request one of the withheld host permissions and an optional host |
| // permission in the same call. |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE(RunRequestFunction( |
| *extension, browser(), |
| R"([{"origins": ["https://example.com/*", "https://chromium.org/*"]}])", |
| &prompted_permissions)); |
| ASSERT_TRUE(prompted_permissions); |
| EXPECT_THAT(GetPatternsAsStrings(prompted_permissions->effective_hosts()), |
| testing::UnorderedElementsAre("https://chromium.org/*", |
| "https://example.com/*")); |
| |
| // The requested permissions should be added. |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kExampleCom)); |
| EXPECT_FALSE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kGoogleCom)); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kChromiumOrg)); |
| } |
| |
| // Tests requesting permissions that weren't specified in the manifest (either |
| // in optional permissions or in required permissions). |
| TEST_F(PermissionsAPIUnitTest, RequestingPermissionsNotSpecifiedInManifest) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| // Create an extension with required and optional host permissions, and |
| // withhold the required permissions. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermissions({ |
| "https://example.com/*", |
| }) |
| .SetManifestKey("optional_permissions", |
| StringVectorToValue({"https://chromium.org/*"})) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier(profile(), extension) |
| .SetWithholdHostPermissions(true); |
| |
| const GURL kExampleCom("https://example.com"); |
| const GURL kGoogleCom("https://google.com"); |
| const GURL kChromiumOrg("https://chromium.org"); |
| |
| // Request permission for an optional and required permission, as well as a |
| // permission that wasn't specified in the manifest. The call should fail. |
| // Note: Not using RunRequestFunction(), since that expects function success. |
| auto function = base::MakeRefCounted<PermissionsRequestFunction>(); |
| function->set_user_gesture(true); |
| function->set_extension(extension.get()); |
| EXPECT_EQ("Only permissions specified in the manifest may be requested.", |
| extension_function_test_utils::RunFunctionAndReturnError( |
| function.get(), |
| R"([{ |
| "origins": [ |
| "https://example.com/*", |
| "https://chromium.org/*", |
| "https://google.com/*" |
| ] |
| }])", |
| browser(), api_test_utils::NONE)); |
| } |
| |
| // Tests requesting withheld permissions that have already been granted. |
| TEST_F(PermissionsAPIUnitTest, RequestingAlreadyGrantedWithheldPermissions) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| extensions_features::kRuntimeHostPermissions); |
| |
| // Create an extension with required host permissions, withhold host |
| // permissions, and then grant one of the hosts. |
| scoped_refptr<const Extension> extension = |
| ExtensionBuilder("extension") |
| .AddPermissions({"https://example.com/*", "https://google.com/*"}) |
| .Build(); |
| AddExtensionAndGrantPermissions(*extension); |
| ScriptingPermissionsModifier modifier(profile(), extension); |
| modifier.SetWithholdHostPermissions(true); |
| |
| const GURL kExampleCom("https://example.com"); |
| const GURL kGoogleCom("https://google.com"); |
| modifier.GrantHostPermission(kExampleCom); |
| |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kExampleCom)); |
| EXPECT_FALSE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kGoogleCom)); |
| |
| // Request the already-granted host permission. The function should succeed |
| // (without even prompting the user), and the permission should (still) be |
| // granted. |
| PermissionsRequestFunction::SetAutoConfirmForTests(false); |
| |
| std::unique_ptr<const PermissionSet> prompted_permissions; |
| EXPECT_TRUE(RunRequestFunction(*extension, browser(), |
| R"([{"origins": ["https://example.com/*"]}])", |
| &prompted_permissions)); |
| ASSERT_FALSE(prompted_permissions); |
| |
| // The withheld permission should be granted. |
| EXPECT_TRUE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kExampleCom)); |
| EXPECT_FALSE( |
| permissions_data->active_permissions().effective_hosts().MatchesURL( |
| kGoogleCom)); |
| } |
| |
| } // namespace extensions |