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(
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)
//////////////////////////////////////////////////////////////////////////////
// Reporting cache.
......
......@@ -107,7 +107,8 @@ class CONTENT_EXPORT BrowsingDataRemoverImpl
kAuthCache = 9,
kCodeCaches = 10,
kNetworkErrorLogging = 11,
kMaxValue = kNetworkErrorLogging,
kTrustTokens = 12,
kMaxValue = kTrustTokens,
};
// Represents a single removal task. Contains all parameters needed to execute
......
......@@ -28,6 +28,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/task/post_task.h"
#include "base/test/gmock_callback_support.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/public/browser/browser_context.h"
......@@ -55,6 +56,7 @@
#include "ppapi/buildflags/buildflags.h"
#include "services/network/cookie_manager.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 "storage/browser/test/mock_special_storage_policy.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -72,6 +74,7 @@
#include "net/reporting/reporting_test_util.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
using base::test::RunOnceClosure;
using testing::_;
using testing::ByRef;
using testing::Eq;
......@@ -84,6 +87,8 @@ using testing::MatchResultListener;
using testing::Not;
using testing::Return;
using testing::SizeIs;
using testing::StrictMock;
using testing::Truly;
using testing::UnorderedElementsAre;
using testing::WithArgs;
using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr;
......@@ -145,7 +150,9 @@ net::CanonicalCookie CreateCookieWithHost(const url::Origin& origin) {
class StoragePartitionRemovalTestStoragePartition
: public TestStoragePartition {
public:
StoragePartitionRemovalTestStoragePartition() = default;
StoragePartitionRemovalTestStoragePartition() {
set_network_context(&network_context_);
}
~StoragePartitionRemovalTestStoragePartition() override = default;
void ClearDataForOrigin(uint32_t remove_mask,
......@@ -206,6 +213,7 @@ class StoragePartitionRemovalTestStoragePartition
private:
StoragePartitionRemovalData storage_partition_removal_data_;
network::TestNetworkContext network_context_;
DISALLOW_COPY_AND_ASSIGN(StoragePartitionRemovalTestStoragePartition);
};
......@@ -326,9 +334,13 @@ class BrowsingDataRemoverImplTest : public testing::Test {
const base::Time& delete_end,
int remove_mask,
bool include_protected_origins) {
// TODO(msramek): Consider moving |storage_partition| to the test fixture.
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);
int origin_type_mask = BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
......@@ -351,6 +363,11 @@ class BrowsingDataRemoverImplTest : public testing::Test {
int remove_mask,
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
StoragePartitionRemovalTestStoragePartition storage_partition;
if (network_context_override_) {
storage_partition.set_network_context(network_context_override_);
}
remover_->OverrideStoragePartitionForTesting(&storage_partition);
BrowsingDataRemoverCompletionObserver completion_observer(remover_);
......@@ -392,6 +409,10 @@ class BrowsingDataRemoverImplTest : public testing::Test {
return mock_policy_.get();
}
void set_network_context_override(network::mojom::NetworkContext* context) {
network_context_override_ = context;
}
bool Match(const GURL& origin,
int mask,
storage::SpecialStoragePolicy* policy) {
......@@ -406,6 +427,8 @@ class BrowsingDataRemoverImplTest : public testing::Test {
BrowserTaskEnvironment task_environment_;
std::unique_ptr<BrowserContext> browser_context_;
network::mojom::NetworkContext* network_context_override_ = nullptr;
StoragePartitionRemovalData storage_partition_removal_data_;
scoped_refptr<storage::MockSpecialStoragePolicy> mock_policy_;
......@@ -1479,4 +1502,97 @@ TEST_F(BrowsingDataRemoverImplTest, MultipleTasksInQuickSuccession) {
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
......@@ -663,6 +663,21 @@ bool NetworkContext::SkipReportingPermissionCheck() const {
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(
base::Time time,
base::OnceClosure completion_callback) {
......
......@@ -198,6 +198,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
void GetHasTrustTokensAnswerer(
mojo::PendingReceiver<mojom::HasTrustTokensAnswerer> receiver,
const url::Origin& top_frame_origin) override;
void ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
base::OnceClosure done) override;
void ClearNetworkingHistorySince(
base::Time time,
base::OnceClosure completion_callback) override;
......
......@@ -883,6 +883,12 @@ interface NetworkContext {
pending_receiver<HasTrustTokensAnswerer> has_trust_tokens_answerer,
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
// to events that happened prior to |start_time| may be retained. Only applies
// to network objects without more specific methods (Cookies,
......
......@@ -89,6 +89,8 @@ class TestNetworkContext : public mojom::NetworkContext {
mojom::ClearDataFilterPtr filter,
DomainReliabilityClearMode mode,
ClearDomainReliabilityCallback callback) override {}
void ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
ClearTrustTokenDataCallback callback) override {}
void GetDomainReliabilityJSON(
GetDomainReliabilityJSONCallback callback) override {}
void QueueReport(const std::string& type,
......
......@@ -268,16 +268,20 @@ TrustTokenStore::RetrieveNonstaleRedemptionRecord(
}
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
// |filter| specifies.
// |filter| specifies. (Default to deleting everything, because a null
// |filter| is a wildcard.)
auto matcher = base::BindRepeating(
[](const mojom::ClearDataFilter& filter,
const SuitableTrustTokenOrigin& storage_key) -> bool {
// Match an origin if
// - it is an eTLD+1 (aka "domain and registry") match with anything on
// |filter|'s domain list, or
// - it is an eTLD+1 (aka "domain and registry") match with anything
// on |filter|'s domain list, or
// - it is an origin match with anything on |filter|'s origin list.
bool is_match = base::Contains(filter.origins, storage_key.origin());
......
......@@ -200,6 +200,9 @@ class TrustTokenStore {
// 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.)
//
// |filter| is allowed to be null: nullptr is a wildcard filter indicating
// that all data should be cleared.
//
// Returns whether any data was deleted.
WARN_UNUSED_RESULT virtual bool ClearDataForFilter(
mojom::ClearDataFilterPtr filter);
......
......@@ -642,5 +642,28 @@ TEST(TrustTokenStore, RemovesDataForInvertedFilters) {
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 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