Commit c67c5b5e authored by David Van Cleve's avatar David Van Cleve Committed by Commit Bot

Trust Tokens: Add a data removal interface

This is part of a sequence of CLs implementing data removal logic for
Trust Tokens [*], a prototype feature that enables annotating requests
with some lightweight cryptographic state. The internal storage stores
the results of these cryptographic operations, including some state
necessary for maintaining privacy invariants.

The first CL in the series expanded the Trust Tokens storage system to
satisfy deletion requests parameterized by a
network::mojom::ClearDataFilter. This CL extends NetworkContext with a
Trust Tokens deletion interface and adds a call to this interface in
BrowsingDataRemoverImpl.

The control flow is quite similar to that for clearing reporting data
(which is also scoped per-NetworkContext and deleted from
BrowsingDataRemoverImpl whenever cookies are cleared).

[*]
https://docs.google.com/document/u/1/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit

R=csharrison

Bug: 1064747
Change-Id: I989790c932d1fff2f2fe6245b9c66b456f07b31b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2120102Reviewed-by: default avatarMatthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Reviewed-by: default avatarMaksim Orlovich <morlovich@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Commit-Queue: David Van Cleve <davidvc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758983}
parent 84cb3aef
...@@ -466,6 +466,23 @@ void BrowsingDataRemoverImpl::RemoveImpl( ...@@ -466,6 +466,23 @@ void BrowsingDataRemoverImpl::RemoveImpl(
RenderFrameHostImpl::ClearAllPrefetchedSignedExchangeCache(); RenderFrameHostImpl::ClearAllPrefetchedSignedExchangeCache();
} }
//////////////////////////////////////////////////////////////////////////////
// Prototype Trust Token API (https://github.com/wicg/trust-token-api).
// We don't support clearing data for specific time ranges because much Trust
// Tokens state (e.g. issuers associated with each top-level origin) has no
// notion of associated creation time. Consequently, like for reporting and
// network error logging below, a data removal request for certain
// sites/origins that has the Trust Tokens type in scope will clear all Trust
// Tokens data associated with the requested sites/origins.
if (remove_mask & DATA_TYPE_COOKIES) {
network::mojom::NetworkContext* network_context =
storage_partition->GetNetworkContext();
network_context->ClearTrustTokenData(
filter_builder->BuildNetworkServiceFilter(),
CreateTaskCompletionClosureForMojo(TracingDataType::kTrustTokens));
}
#if BUILDFLAG(ENABLE_REPORTING) #if BUILDFLAG(ENABLE_REPORTING)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Reporting cache. // Reporting cache.
......
...@@ -107,7 +107,8 @@ class CONTENT_EXPORT BrowsingDataRemoverImpl ...@@ -107,7 +107,8 @@ class CONTENT_EXPORT BrowsingDataRemoverImpl
kAuthCache = 9, kAuthCache = 9,
kCodeCaches = 10, kCodeCaches = 10,
kNetworkErrorLogging = 11, kNetworkErrorLogging = 11,
kMaxValue = kNetworkErrorLogging, kTrustTokens = 12,
kMaxValue = kTrustTokens,
}; };
// Represents a single removal task. Contains all parameters needed to execute // Represents a single removal task. Contains all parameters needed to execute
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h" #include "base/task/cancelable_task_tracker.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/test/gmock_callback_support.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
#include "ppapi/buildflags/buildflags.h" #include "ppapi/buildflags/buildflags.h"
#include "services/network/cookie_manager.h" #include "services/network/cookie_manager.h"
#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/test/test_network_context.h" #include "services/network/test/test_network_context.h"
#include "storage/browser/test/mock_special_storage_policy.h" #include "storage/browser/test/mock_special_storage_policy.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
...@@ -72,6 +74,7 @@ ...@@ -72,6 +74,7 @@
#include "net/reporting/reporting_test_util.h" #include "net/reporting/reporting_test_util.h"
#endif // BUILDFLAG(ENABLE_REPORTING) #endif // BUILDFLAG(ENABLE_REPORTING)
using base::test::RunOnceClosure;
using testing::_; using testing::_;
using testing::ByRef; using testing::ByRef;
using testing::Eq; using testing::Eq;
...@@ -84,6 +87,8 @@ using testing::MatchResultListener; ...@@ -84,6 +87,8 @@ using testing::MatchResultListener;
using testing::Not; using testing::Not;
using testing::Return; using testing::Return;
using testing::SizeIs; using testing::SizeIs;
using testing::StrictMock;
using testing::Truly;
using testing::UnorderedElementsAre; using testing::UnorderedElementsAre;
using testing::WithArgs; using testing::WithArgs;
using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr; using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr;
...@@ -145,7 +150,9 @@ net::CanonicalCookie CreateCookieWithHost(const url::Origin& origin) { ...@@ -145,7 +150,9 @@ net::CanonicalCookie CreateCookieWithHost(const url::Origin& origin) {
class StoragePartitionRemovalTestStoragePartition class StoragePartitionRemovalTestStoragePartition
: public TestStoragePartition { : public TestStoragePartition {
public: public:
StoragePartitionRemovalTestStoragePartition() = default; StoragePartitionRemovalTestStoragePartition() {
set_network_context(&network_context_);
}
~StoragePartitionRemovalTestStoragePartition() override = default; ~StoragePartitionRemovalTestStoragePartition() override = default;
void ClearDataForOrigin(uint32_t remove_mask, void ClearDataForOrigin(uint32_t remove_mask,
...@@ -206,6 +213,7 @@ class StoragePartitionRemovalTestStoragePartition ...@@ -206,6 +213,7 @@ class StoragePartitionRemovalTestStoragePartition
private: private:
StoragePartitionRemovalData storage_partition_removal_data_; StoragePartitionRemovalData storage_partition_removal_data_;
network::TestNetworkContext network_context_;
DISALLOW_COPY_AND_ASSIGN(StoragePartitionRemovalTestStoragePartition); DISALLOW_COPY_AND_ASSIGN(StoragePartitionRemovalTestStoragePartition);
}; };
...@@ -326,9 +334,13 @@ class BrowsingDataRemoverImplTest : public testing::Test { ...@@ -326,9 +334,13 @@ class BrowsingDataRemoverImplTest : public testing::Test {
const base::Time& delete_end, const base::Time& delete_end,
int remove_mask, int remove_mask,
bool include_protected_origins) { bool include_protected_origins) {
// TODO(msramek): Consider moving |storage_partition| to the test fixture.
StoragePartitionRemovalTestStoragePartition storage_partition; StoragePartitionRemovalTestStoragePartition storage_partition;
network::TestNetworkContext nop_network_context;
storage_partition.set_network_context(&nop_network_context); if (network_context_override_) {
storage_partition.set_network_context(network_context_override_);
}
remover_->OverrideStoragePartitionForTesting(&storage_partition); remover_->OverrideStoragePartitionForTesting(&storage_partition);
int origin_type_mask = BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB; int origin_type_mask = BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
...@@ -351,6 +363,11 @@ class BrowsingDataRemoverImplTest : public testing::Test { ...@@ -351,6 +363,11 @@ class BrowsingDataRemoverImplTest : public testing::Test {
int remove_mask, int remove_mask,
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) { std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
StoragePartitionRemovalTestStoragePartition storage_partition; StoragePartitionRemovalTestStoragePartition storage_partition;
if (network_context_override_) {
storage_partition.set_network_context(network_context_override_);
}
remover_->OverrideStoragePartitionForTesting(&storage_partition); remover_->OverrideStoragePartitionForTesting(&storage_partition);
BrowsingDataRemoverCompletionObserver completion_observer(remover_); BrowsingDataRemoverCompletionObserver completion_observer(remover_);
...@@ -392,6 +409,10 @@ class BrowsingDataRemoverImplTest : public testing::Test { ...@@ -392,6 +409,10 @@ class BrowsingDataRemoverImplTest : public testing::Test {
return mock_policy_.get(); return mock_policy_.get();
} }
void set_network_context_override(network::mojom::NetworkContext* context) {
network_context_override_ = context;
}
bool Match(const GURL& origin, bool Match(const GURL& origin,
int mask, int mask,
storage::SpecialStoragePolicy* policy) { storage::SpecialStoragePolicy* policy) {
...@@ -406,6 +427,8 @@ class BrowsingDataRemoverImplTest : public testing::Test { ...@@ -406,6 +427,8 @@ class BrowsingDataRemoverImplTest : public testing::Test {
BrowserTaskEnvironment task_environment_; BrowserTaskEnvironment task_environment_;
std::unique_ptr<BrowserContext> browser_context_; std::unique_ptr<BrowserContext> browser_context_;
network::mojom::NetworkContext* network_context_override_ = nullptr;
StoragePartitionRemovalData storage_partition_removal_data_; StoragePartitionRemovalData storage_partition_removal_data_;
scoped_refptr<storage::MockSpecialStoragePolicy> mock_policy_; scoped_refptr<storage::MockSpecialStoragePolicy> mock_policy_;
...@@ -1479,4 +1502,97 @@ TEST_F(BrowsingDataRemoverImplTest, MultipleTasksInQuickSuccession) { ...@@ -1479,4 +1502,97 @@ TEST_F(BrowsingDataRemoverImplTest, MultipleTasksInQuickSuccession) {
EXPECT_FALSE(remover->IsRemovingForTesting()); EXPECT_FALSE(remover->IsRemovingForTesting());
} }
namespace {
class MockNetworkContext : public network::TestNetworkContext {
public:
MOCK_METHOD2(
ClearTrustTokenData,
void(network::mojom::ClearDataFilterPtr,
network::mojom::NetworkContext::ClearTrustTokenDataCallback));
};
} // namespace
TEST_F(BrowsingDataRemoverImplTest, ClearsTrustTokens) {
MockNetworkContext context;
set_network_context_override(&context);
EXPECT_CALL(context, ClearTrustTokenData(_, _)).WillOnce(RunOnceClosure<1>());
// Trust Tokens storage is cleared whenever cookies are cleared.
BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
BrowsingDataRemover::DATA_TYPE_COOKIES,
/*include_protected_origins=*/false);
}
TEST_F(BrowsingDataRemoverImplTest, PreservesTrustTokens) {
StrictMock<MockNetworkContext> context;
set_network_context_override(&context);
// When cookies aren't cleared, Trust Tokens state shouldn't be.
BlockUntilBrowsingDataRemoved(
base::Time(), base::Time::Max(),
BrowsingDataRemover::DATA_TYPE_CACHE, // arbitrary non-cookie type
/*include_protected_origins=*/false);
// (The strict mock will fail the test if its mocked method is called.)
}
TEST_F(BrowsingDataRemoverImplTest, ClearsTrustTokensForSite) {
MockNetworkContext context;
set_network_context_override(&context);
auto expected = network::mojom::ClearDataFilter::New();
expected->domains = {"host1.com"};
EXPECT_CALL(
context,
ClearTrustTokenData(
Truly([&expected](const network::mojom::ClearDataFilterPtr& filter) {
return mojo::Equals(filter, expected);
}),
_))
.WillOnce(RunOnceClosure<1>());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
builder->AddRegisterableDomain("host1.com");
// Trust Tokens storage is cleared whenever cookies are cleared: when clearing
// cookies for a site, we should clear Trust Tokens state for the site.
BlockUntilOriginDataRemoved(base::Time(), base::Time::Max(),
BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder));
}
TEST_F(BrowsingDataRemoverImplTest, ClearsTrustTokensForSiteDespiteTimeRange) {
MockNetworkContext context;
set_network_context_override(&context);
auto expected = network::mojom::ClearDataFilter::New();
expected->domains = {"host1.com"};
EXPECT_CALL(
context,
ClearTrustTokenData(
Truly([&expected](const network::mojom::ClearDataFilterPtr& filter) {
return mojo::Equals(filter, expected);
}),
_))
.WillOnce(RunOnceClosure<1>());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
builder->AddRegisterableDomain("host1.com");
// Trust Tokens storage is cleared whenever cookies are cleared: when clearing
// cookies for a site, we should clear Trust Tokens state for the site.
//
// Since Trust Tokens data is not associated with particular timestamps, we
// should observe the same clearing behavior with a non-default time range as
// with the default time range.
BlockUntilOriginDataRemoved(
base::Time(), base::Time() + base::TimeDelta::FromSeconds(1),
BrowsingDataRemover::DATA_TYPE_COOKIES, std::move(builder));
}
} // namespace content } // namespace content
...@@ -663,6 +663,21 @@ bool NetworkContext::SkipReportingPermissionCheck() const { ...@@ -663,6 +663,21 @@ bool NetworkContext::SkipReportingPermissionCheck() const {
return params_ && params_->skip_reporting_send_permission_check; return params_ && params_->skip_reporting_send_permission_check;
} }
void NetworkContext::ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
base::OnceClosure done) {
if (!trust_token_store_) {
std::move(done).Run();
return;
}
trust_token_store_->ExecuteOrEnqueue(base::BindOnce(
[](mojom::ClearDataFilterPtr filter, base::OnceClosure done,
TrustTokenStore* store) {
ignore_result(store->ClearDataForFilter(std::move(filter)));
std::move(done).Run();
},
std::move(filter), std::move(done)));
}
void NetworkContext::ClearNetworkingHistorySince( void NetworkContext::ClearNetworkingHistorySince(
base::Time time, base::Time time,
base::OnceClosure completion_callback) { base::OnceClosure completion_callback) {
......
...@@ -198,6 +198,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext ...@@ -198,6 +198,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
void GetHasTrustTokensAnswerer( void GetHasTrustTokensAnswerer(
mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver, mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver,
const url::Origin& top_frame_origin) override; const url::Origin& top_frame_origin) override;
void ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
base::OnceClosure done) override;
void ClearNetworkingHistorySince( void ClearNetworkingHistorySince(
base::Time time, base::Time time,
base::OnceClosure completion_callback) override; base::OnceClosure completion_callback) override;
......
...@@ -883,6 +883,12 @@ interface NetworkContext { ...@@ -883,6 +883,12 @@ interface NetworkContext {
pending_receiver<HasTrustTokensAnswerer> has_trust_tokens_answerer, pending_receiver<HasTrustTokensAnswerer> has_trust_tokens_answerer,
url.mojom.Origin top_frame_origin); url.mojom.Origin top_frame_origin);
// Clears persistent Trust Tokens data keyed by the origins in |filter|, and
// by origins corresponding to the domains in |filter|.
//
// A null |filter| indicates that all Trust Tokens data should be cleared.
ClearTrustTokenData(ClearDataFilter? filter) => ();
// Clears network objects with implicit URL history information. Data related // Clears network objects with implicit URL history information. Data related
// to events that happened prior to |start_time| may be retained. Only applies // to events that happened prior to |start_time| may be retained. Only applies
// to network objects without more specific methods (Cookies, // to network objects without more specific methods (Cookies,
......
...@@ -89,6 +89,8 @@ class TestNetworkContext : public mojom::NetworkContext { ...@@ -89,6 +89,8 @@ class TestNetworkContext : public mojom::NetworkContext {
mojom::ClearDataFilterPtr filter, mojom::ClearDataFilterPtr filter,
DomainReliabilityClearMode mode, DomainReliabilityClearMode mode,
ClearDomainReliabilityCallback callback) override {} ClearDomainReliabilityCallback callback) override {}
void ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
ClearTrustTokenDataCallback callback) override {}
void GetDomainReliabilityJSON( void GetDomainReliabilityJSON(
GetDomainReliabilityJSONCallback callback) override {} GetDomainReliabilityJSONCallback callback) override {}
void QueueReport(const std::string& type, void QueueReport(const std::string& type,
......
...@@ -268,16 +268,20 @@ TrustTokenStore::RetrieveNonstaleRedemptionRecord( ...@@ -268,16 +268,20 @@ TrustTokenStore::RetrieveNonstaleRedemptionRecord(
} }
bool TrustTokenStore::ClearDataForFilter(mojom::ClearDataFilterPtr filter) { bool TrustTokenStore::ClearDataForFilter(mojom::ClearDataFilterPtr filter) {
DCHECK(filter); if (!filter) {
return persister_->DeleteForOrigins(base::BindRepeating(
[](const SuitableTrustTokenOrigin&) { return true; }));
}
// Returns whether |storage_key|'s data should be deleted, based on the logic // Returns whether |storage_key|'s data should be deleted, based on the logic
// |filter| specifies. // |filter| specifies. (Default to deleting everything, because a null
// |filter| is a wildcard.)
auto matcher = base::BindRepeating( auto matcher = base::BindRepeating(
[](const mojom::ClearDataFilter& filter, [](const mojom::ClearDataFilter& filter,
const SuitableTrustTokenOrigin& storage_key) -> bool { const SuitableTrustTokenOrigin& storage_key) -> bool {
// Match an origin if // Match an origin if
// - it is an eTLD+1 (aka "domain and registry") match with anything on // - it is an eTLD+1 (aka "domain and registry") match with anything
// |filter|'s domain list, or // on |filter|'s domain list, or
// - it is an origin match with anything on |filter|'s origin list. // - it is an origin match with anything on |filter|'s origin list.
bool is_match = base::Contains(filter.origins, storage_key.origin()); bool is_match = base::Contains(filter.origins, storage_key.origin());
......
...@@ -200,6 +200,9 @@ class TrustTokenStore { ...@@ -200,6 +200,9 @@ class TrustTokenStore {
// matching the filter. (In particular, this will still delete data keyed by // matching the filter. (In particular, this will still delete data keyed by
// a pair of origins, one of which matches and one of which does not.) // a pair of origins, one of which matches and one of which does not.)
// //
// |filter| is allowed to be null: nullptr is a wildcard filter indicating
// that all data should be cleared.
//
// Returns whether any data was deleted. // Returns whether any data was deleted.
WARN_UNUSED_RESULT virtual bool ClearDataForFilter( WARN_UNUSED_RESULT virtual bool ClearDataForFilter(
mojom::ClearDataFilterPtr filter); mojom::ClearDataFilterPtr filter);
......
...@@ -642,5 +642,28 @@ TEST(TrustTokenStore, RemovesDataForInvertedFilters) { ...@@ -642,5 +642,28 @@ TEST(TrustTokenStore, RemovesDataForInvertedFilters) {
EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel)); EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel));
} }
TEST(TrustTokenStore, RemovesDataForNullFilter) {
// A null filter is a "clear all data" wildcard.
auto store = TrustTokenStore::CreateInMemory();
auto issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://www.issuer.com"));
auto toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://www.toplevel.com"));
// Add some issuer-keyed state,
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
// some top level-keyed state,
ASSERT_TRUE(store->SetAssociation(issuer, toplevel));
// and some (issuer, top level) pair-keyed state.
store->SetRedemptionRecord(issuer, toplevel,
SignedTrustTokenRedemptionRecord{});
EXPECT_TRUE(store->ClearDataForFilter(nullptr));
EXPECT_FALSE(store->CountTokens(issuer));
EXPECT_FALSE(store->IsAssociated(issuer, toplevel));
EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel));
}
} // namespace trust_tokens } // namespace trust_tokens
} // namespace network } // namespace network
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