* [CodeHealth] Refactor permissions origin 1) Pass origin to dApp on construction instead of resolving it from delegate. 2) Pass origin to iOS panel ui from the core side.
453 lines
18 KiB
C++
453 lines
18 KiB
C++
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "brave/components/brave_wallet/common/buildflags/buildflags.h"
|
|
|
|
#if BUILDFLAG(ENABLE_BRAVE_WALLET)
|
|
|
|
#include <array>
|
|
|
|
#include "base/functional/callback_helpers.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/test/test_future.h"
|
|
#include "base/types/zip.h"
|
|
#include "brave/components/brave_wallet/browser/permission_utils.h"
|
|
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
|
|
#include "brave/components/permissions/contexts/brave_wallet_permission_context.h"
|
|
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
|
|
#include "chrome/browser/permissions/permission_manager_factory.h"
|
|
#include "chrome/browser/profiles/profile.h"
|
|
#include "chrome/browser/ui/browser.h"
|
|
#include "chrome/browser/ui/tabs/tab_enums.h"
|
|
#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
|
#include "chrome/test/base/in_process_browser_test.h"
|
|
#include "chrome/test/base/ui_test_utils.h"
|
|
#include "components/content_settings/core/browser/host_content_settings_map.h"
|
|
#include "components/content_settings/core/common/content_settings.h"
|
|
#include "components/content_settings/core/common/content_settings_types.h"
|
|
#include "components/permissions/permission_manager.h"
|
|
#include "components/permissions/permission_request_manager.h"
|
|
#include "components/permissions/permissions_client.h"
|
|
#include "components/permissions/request_type.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "content/public/test/browser_test.h"
|
|
#include "content/public/test/browser_test_utils.h"
|
|
#include "content/public/test/test_utils.h"
|
|
#include "net/dns/mock_host_resolver.h"
|
|
#include "net/test/embedded_test_server/embedded_test_server.h"
|
|
#include "url/gurl.h"
|
|
#include "url/origin.h"
|
|
|
|
using base::test::TestFuture;
|
|
using testing::ElementsAreArray;
|
|
|
|
namespace permissions {
|
|
|
|
namespace {
|
|
|
|
class PermissionRequestManagerObserver
|
|
: public PermissionRequestManager::Observer {
|
|
public:
|
|
explicit PermissionRequestManagerObserver(PermissionRequestManager* manager)
|
|
: manager_(manager) {
|
|
manager_->AddObserver(this);
|
|
}
|
|
|
|
~PermissionRequestManagerObserver() override {
|
|
manager_->RemoveObserver(this);
|
|
}
|
|
|
|
// PermissionRequestManager::Observer:
|
|
void OnPromptAdded() override { is_showing_bubble_ = true; }
|
|
void OnPromptRemoved() override { is_showing_bubble_ = false; }
|
|
void OnRequestsFinalized() override { is_requests_finalized_ = true; }
|
|
|
|
bool IsShowingBubble() { return is_showing_bubble_; }
|
|
bool IsRequestsFinalized() { return is_requests_finalized_; }
|
|
void Reset() { is_showing_bubble_ = is_requests_finalized_ = false; }
|
|
|
|
private:
|
|
raw_ptr<PermissionRequestManager> manager_ = nullptr;
|
|
bool is_showing_bubble_ = false;
|
|
bool is_requests_finalized_ = false;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class PermissionManagerBrowserTest : public InProcessBrowserTest {
|
|
public:
|
|
PermissionManagerBrowserTest()
|
|
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
|
|
|
|
~PermissionManagerBrowserTest() override = default;
|
|
|
|
void SetUpOnMainThread() override {
|
|
InProcessBrowserTest::SetUpOnMainThread();
|
|
host_resolver()->AddRule("*", "127.0.0.1");
|
|
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
|
|
https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
|
|
ASSERT_TRUE(https_server()->Start());
|
|
}
|
|
|
|
PermissionRequestManager* GetPermissionRequestManager() {
|
|
return PermissionRequestManager::FromWebContents(
|
|
browser()->tab_strip_model()->GetActiveWebContents());
|
|
}
|
|
|
|
HostContentSettingsMap* host_content_settings_map(Profile* profile) {
|
|
return HostContentSettingsMapFactory::GetForProfile(profile);
|
|
}
|
|
|
|
content::WebContents* web_contents() {
|
|
return browser()->tab_strip_model()->GetActiveWebContents();
|
|
}
|
|
url::Origin GetLastCommitedOrigin() {
|
|
return url::Origin::Create(web_contents()->GetLastCommittedURL());
|
|
}
|
|
|
|
net::EmbeddedTestServer* https_server() { return &https_server_; }
|
|
|
|
protected:
|
|
net::test_server::EmbeddedTestServer https_server_;
|
|
};
|
|
|
|
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest, RequestPermissions) {
|
|
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
|
|
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
|
|
|
|
auto* permission_request_manager = GetPermissionRequestManager();
|
|
EXPECT_FALSE(permission_request_manager->IsRequestInProgress());
|
|
struct TestEntries {
|
|
std::vector<std::string> addresses;
|
|
ContentSettingsType type;
|
|
blink::PermissionType permission;
|
|
};
|
|
auto cases = std::to_array<TestEntries>(
|
|
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A",
|
|
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8B"},
|
|
ContentSettingsType::BRAVE_ETHEREUM,
|
|
blink::PermissionType::BRAVE_ETHEREUM},
|
|
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
|
|
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
|
|
ContentSettingsType::BRAVE_SOLANA,
|
|
blink::PermissionType::BRAVE_SOLANA},
|
|
{{"1815_0_0_0", "1815_1_0_0"},
|
|
ContentSettingsType::BRAVE_CARDANO,
|
|
blink::PermissionType::BRAVE_CARDANO}});
|
|
for (auto& test_case : cases) {
|
|
SCOPED_TRACE(testing::Message() << test_case.type);
|
|
|
|
const std::vector<std::string>& addresses = test_case.addresses;
|
|
RequestType request_type = ContentSettingsTypeToRequestType(test_case.type);
|
|
|
|
std::vector<url::Origin> sub_request_origins;
|
|
for (auto& address : addresses) {
|
|
SCOPED_TRACE(testing::Message() << address);
|
|
auto sub_request_origin = brave_wallet::GetSubRequestOrigin(
|
|
request_type, GetLastCommitedOrigin(), address);
|
|
ASSERT_TRUE(sub_request_origin) << address;
|
|
sub_request_origins.push_back(*sub_request_origin);
|
|
}
|
|
|
|
auto observer = std::make_unique<PermissionRequestManagerObserver>(
|
|
permission_request_manager);
|
|
|
|
TestFuture<std::vector<std::string>> request_permissions_future;
|
|
BraveWalletPermissionContext::RequestWalletPermissions(
|
|
addresses, test_case.permission, url::Origin::Create(url),
|
|
web_contents()->GetPrimaryMainFrame(),
|
|
request_permissions_future.GetCallback());
|
|
|
|
content::RunAllTasksUntilIdle();
|
|
|
|
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
// update anchor should not dismiss the bubble
|
|
permission_request_manager->UpdateAnchor();
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
|
|
// Check sub-requests are created as expected.
|
|
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
|
|
for (const auto [request, sub_request_origin] : base::zip(
|
|
permission_request_manager->Requests(), sub_request_origins)) {
|
|
SCOPED_TRACE(testing::Message() << sub_request_origin);
|
|
|
|
EXPECT_EQ(request->request_type(), request_type);
|
|
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
|
|
}
|
|
|
|
// Test dismissing request.
|
|
permissions::BraveWalletPermissionContext::Cancel(web_contents());
|
|
EXPECT_TRUE(request_permissions_future.Take().empty());
|
|
EXPECT_TRUE(observer->IsRequestsFinalized());
|
|
EXPECT_TRUE(!observer->IsShowingBubble());
|
|
|
|
for (const auto& sub_request_origin : sub_request_origins) {
|
|
SCOPED_TRACE(testing::Message() << sub_request_origin);
|
|
|
|
EXPECT_EQ(host_content_settings_map(browser()->profile())
|
|
->GetContentSetting(sub_request_origin.GetURL(),
|
|
GetLastCommitedOrigin().GetURL(),
|
|
test_case.type),
|
|
ContentSetting::CONTENT_SETTING_ASK);
|
|
}
|
|
|
|
observer->Reset();
|
|
BraveWalletPermissionContext::RequestWalletPermissions(
|
|
addresses, test_case.permission, url::Origin::Create(url),
|
|
web_contents()->GetPrimaryMainFrame(),
|
|
request_permissions_future.GetCallback());
|
|
|
|
content::RunAllTasksUntilIdle();
|
|
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
// update anchor should not dismiss the bubble
|
|
permission_request_manager->UpdateAnchor();
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
|
|
// Check sub-requests are created as expected.
|
|
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
|
|
for (const auto [request, sub_request_origin] : base::zip(
|
|
permission_request_manager->Requests(), sub_request_origins)) {
|
|
SCOPED_TRACE(testing::Message() << sub_request_origin);
|
|
|
|
EXPECT_EQ(request->request_type(), request_type);
|
|
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
|
|
}
|
|
|
|
// Test accepting request with one of the address.
|
|
permissions::BraveWalletPermissionContext::AcceptOrCancel(
|
|
std::vector<std::string>{test_case.addresses[1]},
|
|
brave_wallet::mojom::PermissionLifetimeOption::kForever,
|
|
web_contents());
|
|
EXPECT_THAT(request_permissions_future.Take(),
|
|
ElementsAreArray({test_case.addresses[1]}));
|
|
std::vector<ContentSetting> expected_settings(
|
|
{ContentSetting::CONTENT_SETTING_ASK,
|
|
ContentSetting::CONTENT_SETTING_ALLOW});
|
|
EXPECT_TRUE(observer->IsRequestsFinalized());
|
|
EXPECT_TRUE(!observer->IsShowingBubble());
|
|
|
|
for (const auto [setting, sub_request_origin] :
|
|
base::zip(expected_settings, sub_request_origins)) {
|
|
SCOPED_TRACE(testing::Message() << sub_request_origin);
|
|
|
|
EXPECT_EQ(host_content_settings_map(browser()->profile())
|
|
->GetContentSetting(sub_request_origin.GetURL(),
|
|
GetLastCommitedOrigin().GetURL(),
|
|
test_case.type),
|
|
setting);
|
|
}
|
|
}
|
|
}
|
|
|
|
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest,
|
|
RequestPermissionsTabClosed) {
|
|
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
|
|
|
|
struct TestEntries {
|
|
std::vector<std::string> addresses;
|
|
ContentSettingsType type;
|
|
blink::PermissionType permission;
|
|
};
|
|
auto cases = std::to_array<TestEntries>(
|
|
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8C",
|
|
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8D"},
|
|
ContentSettingsType::BRAVE_ETHEREUM,
|
|
blink::PermissionType::BRAVE_ETHEREUM},
|
|
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
|
|
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
|
|
ContentSettingsType::BRAVE_SOLANA,
|
|
blink::PermissionType::BRAVE_SOLANA},
|
|
{{"1815_1_1_0"},
|
|
ContentSettingsType::BRAVE_CARDANO,
|
|
blink::PermissionType::BRAVE_CARDANO}});
|
|
for (auto& test_case : cases) {
|
|
SCOPED_TRACE(testing::Message() << test_case.type);
|
|
|
|
ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 0, url,
|
|
ui::PAGE_TRANSITION_TYPED, true));
|
|
auto* permission_request_manager = GetPermissionRequestManager();
|
|
EXPECT_FALSE(permission_request_manager->IsRequestInProgress());
|
|
const std::vector<std::string>& addresses = test_case.addresses;
|
|
RequestType request_type = ContentSettingsTypeToRequestType(test_case.type);
|
|
|
|
std::vector<blink::PermissionType> permissions(addresses.size(),
|
|
test_case.permission);
|
|
std::vector<url::Origin> sub_request_origins;
|
|
for (const auto& address : test_case.addresses) {
|
|
url::Origin origin;
|
|
auto sub_request_origin = brave_wallet::GetSubRequestOrigin(
|
|
request_type, GetLastCommitedOrigin(), address);
|
|
ASSERT_TRUE(sub_request_origin) << address;
|
|
sub_request_origins.push_back(*sub_request_origin);
|
|
}
|
|
|
|
auto observer = std::make_unique<PermissionRequestManagerObserver>(
|
|
permission_request_manager);
|
|
|
|
BraveWalletPermissionContext::RequestWalletPermissions(
|
|
addresses, test_case.permission, url::Origin::Create(url),
|
|
web_contents()->GetPrimaryMainFrame(), base::DoNothing());
|
|
content::RunAllTasksUntilIdle();
|
|
|
|
EXPECT_TRUE(permission_request_manager->IsRequestInProgress());
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
// update anchor should not dismiss the bubble
|
|
permission_request_manager->UpdateAnchor();
|
|
EXPECT_TRUE(observer->IsShowingBubble());
|
|
|
|
// Check sub-requests are created as expected.
|
|
EXPECT_EQ(permission_request_manager->Requests().size(), addresses.size());
|
|
for (const auto [request, sub_request_origin] : base::zip(
|
|
permission_request_manager->Requests(), sub_request_origins)) {
|
|
SCOPED_TRACE(testing::Message() << sub_request_origin);
|
|
|
|
EXPECT_EQ(request->request_type(), request_type);
|
|
EXPECT_EQ(sub_request_origin.GetURL(), request->requesting_origin());
|
|
}
|
|
|
|
// Remove the observer before closing the tab.
|
|
observer.reset();
|
|
|
|
// Close tab with active request pending.
|
|
content::WebContentsDestroyedWatcher tab_destroyed_watcher(web_contents());
|
|
browser()->tab_strip_model()->CloseWebContentsAt(0,
|
|
TabCloseTypes::CLOSE_NONE);
|
|
tab_destroyed_watcher.Wait();
|
|
}
|
|
}
|
|
|
|
IN_PROC_BROWSER_TEST_F(PermissionManagerBrowserTest, GetCanonicalOrigin) {
|
|
const GURL& url = https_server()->GetURL("a.test", "/empty.html");
|
|
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
|
|
|
|
struct TestEntries {
|
|
std::vector<std::string> addresses;
|
|
ContentSettingsType type;
|
|
};
|
|
auto cases = std::to_array<TestEntries>(
|
|
{{{"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A",
|
|
"0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8B"},
|
|
ContentSettingsType::BRAVE_ETHEREUM},
|
|
{{"BrG44HdsEhzapvs8bEqzvkq4egwevS3fRE6ze2ENo6S8",
|
|
"JDqrvDz8d8tFCADashbUKQDKfJZFobNy13ugN65t1wvV"},
|
|
ContentSettingsType::BRAVE_SOLANA},
|
|
{{"addr1q8gg2r3vf9zggn48g7m8vx62rwf6warcs4k7ej8mdzmqmesj30jz7psduyk6n4n2"
|
|
"qrud2xlv9fgj53n6ds3t8cs4fvzs05yzmz",
|
|
"Ae2tdPwUPEZFSi1cTyL1ZL6bgixhc2vSy5heg6Zg9uP7PpumkAJ82Qprt8b"},
|
|
ContentSettingsType::BRAVE_CARDANO}});
|
|
for (auto& test_case : cases) {
|
|
SCOPED_TRACE(testing::Message() << test_case.type);
|
|
|
|
for (auto& address : test_case.addresses) {
|
|
auto origin = brave_wallet::GetSubRequestOrigin(
|
|
ContentSettingsTypeToRequestType(test_case.type),
|
|
GetLastCommitedOrigin(), address);
|
|
ASSERT_TRUE(origin);
|
|
|
|
EXPECT_EQ(origin->GetURL(),
|
|
permissions::PermissionUtil::GetCanonicalOrigin(
|
|
test_case.type, origin->GetURL(),
|
|
GetLastCommitedOrigin().GetURL()))
|
|
<< "GetCanonicalOrigin should return requesting_origin for Ethereum "
|
|
"permission.";
|
|
}
|
|
}
|
|
}
|
|
|
|
class PermissionManagerIncognitoBrowserTest : public InProcessBrowserTest {
|
|
public:
|
|
PermissionManagerIncognitoBrowserTest()
|
|
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
|
|
|
|
~PermissionManagerIncognitoBrowserTest() override = default;
|
|
|
|
void SetUpOnMainThread() override {
|
|
InProcessBrowserTest::SetUpOnMainThread();
|
|
host_resolver()->AddRule("*", "127.0.0.1");
|
|
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
|
|
https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
|
|
ASSERT_TRUE(https_server()->Start());
|
|
}
|
|
|
|
net::EmbeddedTestServer* https_server() { return &https_server_; }
|
|
|
|
void TestRequestPermissionsDoNotLeak(Browser* browser1, Browser* browser2) {
|
|
ASSERT_TRUE(ui_test_utils::NavigateToURL(
|
|
browser1, https_server()->GetURL("a.test", "/empty.html")));
|
|
|
|
auto* web_contents = browser1->tab_strip_model()->GetActiveWebContents();
|
|
auto active_origin =
|
|
url::Origin::Create(web_contents->GetLastCommittedURL());
|
|
|
|
const std::string address = "0xaf5Ad1E10926C0Ee4af4eDAC61DD60E853753f8A";
|
|
ContentSettingsType type = ContentSettingsType::BRAVE_ETHEREUM;
|
|
blink::PermissionType permission = blink::PermissionType::BRAVE_ETHEREUM;
|
|
|
|
RequestType request_type = ContentSettingsTypeToRequestType(type);
|
|
auto sub_request_origin =
|
|
brave_wallet::GetSubRequestOrigin(request_type, active_origin, address);
|
|
ASSERT_TRUE(sub_request_origin);
|
|
|
|
auto observer = std::make_unique<PermissionRequestManagerObserver>(
|
|
PermissionRequestManager::FromWebContents(web_contents));
|
|
|
|
TestFuture<std::vector<std::string>> request_permissions_future;
|
|
|
|
BraveWalletPermissionContext::RequestWalletPermissions(
|
|
{address}, permission, active_origin,
|
|
web_contents->GetPrimaryMainFrame(),
|
|
request_permissions_future.GetCallback());
|
|
|
|
content::RunAllTasksUntilIdle();
|
|
permissions::BraveWalletPermissionContext::AcceptOrCancel(
|
|
{address}, brave_wallet::mojom::PermissionLifetimeOption::kForever,
|
|
web_contents);
|
|
|
|
EXPECT_THAT(request_permissions_future.Take(), ElementsAreArray({address}));
|
|
|
|
EXPECT_TRUE(observer->IsRequestsFinalized());
|
|
EXPECT_TRUE(!observer->IsShowingBubble());
|
|
|
|
// Verify the permission has changed for profile1
|
|
EXPECT_EQ(
|
|
HostContentSettingsMapFactory::GetForProfile(browser1->GetProfile())
|
|
->GetContentSetting(sub_request_origin->GetURL(),
|
|
active_origin.GetURL(), type),
|
|
ContentSetting::CONTENT_SETTING_ALLOW);
|
|
|
|
// Verify the permission hasn't leaked to profile2
|
|
EXPECT_EQ(
|
|
HostContentSettingsMapFactory::GetForProfile(browser2->GetProfile())
|
|
->GetContentSetting(sub_request_origin->GetURL(),
|
|
active_origin.GetURL(), type),
|
|
ContentSetting::CONTENT_SETTING_ASK);
|
|
}
|
|
|
|
protected:
|
|
net::test_server::EmbeddedTestServer https_server_;
|
|
};
|
|
|
|
IN_PROC_BROWSER_TEST_F(PermissionManagerIncognitoBrowserTest,
|
|
IncognitoPermissionsDoNotLeak) {
|
|
auto* incognito_browser = CreateIncognitoBrowser(browser()->profile());
|
|
|
|
// Verify permissions do not leak from incognito profile into normal profile.
|
|
TestRequestPermissionsDoNotLeak(incognito_browser, browser());
|
|
}
|
|
|
|
IN_PROC_BROWSER_TEST_F(PermissionManagerIncognitoBrowserTest,
|
|
PermissionsDoNotLeak) {
|
|
auto* incognito_browser = CreateIncognitoBrowser(browser()->profile());
|
|
|
|
// Verify permissions do not leak from normal profile into incognito profile.
|
|
TestRequestPermissionsDoNotLeak(browser(), incognito_browser);
|
|
}
|
|
|
|
} // namespace permissions
|
|
|
|
#endif // BUILDFLAG(ENABLE_BRAVE_WALLET)
|