Commit 451c64aa authored by Simon Zünd's avatar Simon Zünd Committed by Chromium LUCI CQ

Make Trust Token count per issuer information available for DevTools

Design doc: https://bit.ly/devtools-trust-tokens

This CL adds a new getter to the NetworkContext interface to make
the current state of the Trust Token store available to DevTools.
The information will be shown on the Application tab in DevTools so
developers can inspect the Trust Tokens currently available to their
site.

Bug: chromium:1126824
Change-Id: I69bfc596b1c42fab25bb91d3dd7e3116ec20fd96
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2592990Reviewed-by: default avatarDavid Van Cleve <davidvc@chromium.org>
Reviewed-by: default avatarMatthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842905}
parent eec3b443
...@@ -96,6 +96,7 @@ ...@@ -96,6 +96,7 @@
#include "services/network/public/cpp/parsed_headers.h" #include "services/network/public/cpp/parsed_headers.h"
#include "services/network/public/mojom/network_context.mojom-forward.h" #include "services/network/public/mojom/network_context.mojom-forward.h"
#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/quic_transport.h" #include "services/network/quic_transport.h"
#include "services/network/resolve_host_request.h" #include "services/network/resolve_host_request.h"
...@@ -638,6 +639,29 @@ void NetworkContext::GetHasTrustTokensAnswerer( ...@@ -638,6 +639,29 @@ void NetworkContext::GetHasTrustTokensAnswerer(
has_trust_tokens_answerers_.Add(std::move(answerer), std::move(receiver)); has_trust_tokens_answerers_.Add(std::move(answerer), std::move(receiver));
} }
void NetworkContext::GetStoredTrustTokenCounts(
GetStoredTrustTokenCountsCallback callback) {
if (trust_token_store_) {
auto get_trust_token_counts_from_store =
[](NetworkContext::GetStoredTrustTokenCountsCallback callback,
TrustTokenStore* trust_token_store) {
std::vector<mojom::StoredTrustTokensForIssuerPtr> result;
for (auto& issuer_count_pair :
trust_token_store->GetStoredTrustTokenCounts()) {
result.push_back(mojom::StoredTrustTokensForIssuer::New(
std::move(issuer_count_pair.first), issuer_count_pair.second));
}
std::move(callback).Run(std::move(result));
};
trust_token_store_->ExecuteOrEnqueue(
base::BindOnce(get_trust_token_counts_from_store, std::move(callback)));
} else {
// The Trust Tokens feature is disabled, return immediately with an empty
// vector.
std::move(callback).Run({});
}
}
void NetworkContext::OnProxyLookupComplete( void NetworkContext::OnProxyLookupComplete(
ProxyLookupRequest* proxy_lookup_request) { ProxyLookupRequest* proxy_lookup_request) {
auto it = proxy_lookup_requests_.find(proxy_lookup_request); auto it = proxy_lookup_requests_.find(proxy_lookup_request);
......
...@@ -207,6 +207,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext ...@@ -207,6 +207,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
const url::Origin& top_frame_origin) override; const url::Origin& top_frame_origin) override;
void ClearTrustTokenData(mojom::ClearDataFilterPtr filter, void ClearTrustTokenData(mojom::ClearDataFilterPtr filter,
base::OnceClosure done) override; base::OnceClosure done) override;
void GetStoredTrustTokenCounts(
GetStoredTrustTokenCountsCallback callback) override;
void ClearNetworkingHistoryBetween( void ClearNetworkingHistoryBetween(
base::Time start_time, base::Time start_time,
base::Time end_time, base::Time end_time,
......
...@@ -6899,6 +6899,121 @@ TEST_F(NetworkContextTest, ...@@ -6899,6 +6899,121 @@ TEST_F(NetworkContextTest,
mojom::TrustTokenOperationStatus::kUnavailable); mojom::TrustTokenOperationStatus::kUnavailable);
} }
TEST_F(NetworkContextTest, NoAvailableTrustTokensWhenTrustTokensAreDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(features::kTrustTokens);
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(mojom::NetworkContextParams::New());
// Allow the store time to initialize asynchronously.
base::RunLoop run_loop;
base::Optional<std::vector<mojom::StoredTrustTokensForIssuerPtr>>
trust_tokens;
network_context->GetStoredTrustTokenCounts(base::BindLambdaForTesting(
[&trust_tokens,
&run_loop](std::vector<mojom::StoredTrustTokensForIssuerPtr> tokens) {
trust_tokens = std::move(tokens);
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(trust_tokens.has_value());
EXPECT_TRUE(trust_tokens->empty());
}
TEST_F(NetworkContextTest, GetStoredTrustTokens) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kTrustTokens);
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(mojom::NetworkContextParams::New());
base::RunLoop run_loop;
// Query Trust Tokens before adding the mock token.
base::Optional<std::vector<mojom::StoredTrustTokensForIssuerPtr>>
trust_tokens_before_adding;
network_context->GetStoredTrustTokenCounts(base::BindLambdaForTesting(
[&](std::vector<mojom::StoredTrustTokensForIssuerPtr> tokens) {
trust_tokens_before_adding = std::move(tokens);
}));
// Add a mock token.
network_context->trust_token_store()->ExecuteOrEnqueue(
base::BindLambdaForTesting([&](TrustTokenStore* store) {
DCHECK(store);
store->AddTokens(
*SuitableTrustTokenOrigin::Create(GURL("https://trusttoken.com")),
std::vector<std::string>{"token"}, "issuing key");
}));
// Query Trust Tokens after adding the mock token.
base::Optional<std::vector<mojom::StoredTrustTokensForIssuerPtr>>
trust_tokens_after_adding;
network_context->GetStoredTrustTokenCounts(base::BindLambdaForTesting(
[&](std::vector<mojom::StoredTrustTokensForIssuerPtr> tokens) {
trust_tokens_after_adding = std::move(tokens);
run_loop.Quit();
}));
// Allow the store time to initialize asynchronously and execute the
// operations.
run_loop.Run();
ASSERT_TRUE(trust_tokens_before_adding.has_value());
EXPECT_EQ(trust_tokens_before_adding->size(), 0ul);
ASSERT_TRUE(trust_tokens_after_adding.has_value());
ASSERT_EQ(trust_tokens_after_adding->size(), 1ul);
EXPECT_EQ(trust_tokens_after_adding.value()[0]->issuer.Serialize(),
"https://trusttoken.com");
}
TEST_F(NetworkContextTest, GetStoredTrustTokensReentrant) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kTrustTokens);
std::unique_ptr<NetworkContext> network_context =
CreateContextWithParams(mojom::NetworkContextParams::New());
// Add a mock token.
base::RunLoop run_loop;
network_context->trust_token_store()->ExecuteOrEnqueue(
base::BindLambdaForTesting([&](TrustTokenStore* store) {
DCHECK(store);
store->AddTokens(
*SuitableTrustTokenOrigin::Create(GURL("https://trusttoken.com")),
std::vector<std::string>{"token"}, "issuing key");
}));
base::Optional<std::vector<mojom::StoredTrustTokensForIssuerPtr>>
trust_tokens;
base::Optional<std::vector<mojom::StoredTrustTokensForIssuerPtr>>
reentrant_trust_tokens;
network_context->GetStoredTrustTokenCounts(base::BindLambdaForTesting(
[&](std::vector<mojom::StoredTrustTokensForIssuerPtr> tokens) {
network_context->GetStoredTrustTokenCounts(base::BindLambdaForTesting(
[&](std::vector<mojom::StoredTrustTokensForIssuerPtr> tokens) {
reentrant_trust_tokens = std::move(tokens);
run_loop.Quit();
}));
trust_tokens = std::move(tokens);
}));
// Allow the store time to initialize asynchronously and execute the
// operations.
run_loop.Run();
ASSERT_TRUE(trust_tokens.has_value());
ASSERT_TRUE(reentrant_trust_tokens.has_value());
EXPECT_EQ(trust_tokens->size(), reentrant_trust_tokens->size());
EXPECT_EQ(trust_tokens.value()[0]->issuer,
reentrant_trust_tokens.value()[0]->issuer);
EXPECT_EQ(trust_tokens.value()[0]->count,
reentrant_trust_tokens.value()[0]->count);
}
} // namespace } // namespace
} // namespace network } // namespace network
...@@ -945,6 +945,10 @@ interface NetworkContext { ...@@ -945,6 +945,10 @@ interface NetworkContext {
// A null |filter| indicates that all Trust Tokens data should be cleared. // A null |filter| indicates that all Trust Tokens data should be cleared.
ClearTrustTokenData(ClearDataFilter? filter) => (); ClearTrustTokenData(ClearDataFilter? filter) => ();
// Returns the number of signed-but-not-spent Trust Tokens.
GetStoredTrustTokenCounts()
=> (array<StoredTrustTokensForIssuer> tokens);
// 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| and after |end_time| may be // to events that happened prior to |start_time| and after |end_time| may be
// retained. Only applies to network objects without more specific methods // retained. Only applies to network objects without more specific methods
......
...@@ -279,3 +279,10 @@ struct TrustTokenOperationResult { ...@@ -279,3 +279,10 @@ struct TrustTokenOperationResult {
// In case of TrustTokenOperationType::kIssuance. // In case of TrustTokenOperationType::kIssuance.
int32 issued_token_count = 0; int32 issued_token_count = 0;
}; };
// Struct StoredTrustTokensForIssuer is used by DevTools to inspect
// the current state of the Trust Token store.
struct StoredTrustTokensForIssuer {
url.mojom.Origin issuer;
int32 count;
};
...@@ -64,6 +64,8 @@ class TestNetworkContext : public mojom::NetworkContext { ...@@ -64,6 +64,8 @@ class TestNetworkContext : public mojom::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 GetStoredTrustTokenCounts(
GetStoredTrustTokenCountsCallback callback) override {}
void ClearNetworkingHistoryBetween( void ClearNetworkingHistoryBetween(
base::Time start_time, base::Time start_time,
base::Time end_time, base::Time end_time,
......
...@@ -80,4 +80,13 @@ bool InMemoryTrustTokenPersister::DeleteForOrigins( ...@@ -80,4 +80,13 @@ bool InMemoryTrustTokenPersister::DeleteForOrigins(
return deleted_any_data; return deleted_any_data;
} }
base::flat_map<SuitableTrustTokenOrigin, int>
InMemoryTrustTokenPersister::GetStoredTrustTokenCounts() {
base::flat_map<SuitableTrustTokenOrigin, int> result;
for (const auto& kv : issuer_configs_) {
result.emplace(kv.first, kv.second->tokens_size());
}
return result;
}
} // namespace network } // namespace network
...@@ -49,6 +49,9 @@ class InMemoryTrustTokenPersister : public TrustTokenPersister { ...@@ -49,6 +49,9 @@ class InMemoryTrustTokenPersister : public TrustTokenPersister {
base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)> matcher) base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)> matcher)
override; override;
base::flat_map<SuitableTrustTokenOrigin, int> GetStoredTrustTokenCounts()
override;
private: private:
std::map<SuitableTrustTokenOrigin, std::unique_ptr<TrustTokenToplevelConfig>> std::map<SuitableTrustTokenOrigin, std::unique_ptr<TrustTokenToplevelConfig>>
toplevel_configs_; toplevel_configs_;
......
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "components/sqlite_proto/key_value_data.h" #include "components/sqlite_proto/key_value_data.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/trust_tokens/proto/storage.pb.h" #include "services/network/trust_tokens/proto/storage.pb.h"
#include "services/network/trust_tokens/trust_token_database_owner.h" #include "services/network/trust_tokens/trust_token_database_owner.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -236,4 +238,22 @@ bool SQLiteTrustTokenPersister::DeleteForOrigins( ...@@ -236,4 +238,22 @@ bool SQLiteTrustTokenPersister::DeleteForOrigins(
return any_data_was_deleted; return any_data_was_deleted;
} }
base::flat_map<SuitableTrustTokenOrigin, int>
SQLiteTrustTokenPersister::GetStoredTrustTokenCounts() {
base::flat_map<SuitableTrustTokenOrigin, int> result;
sqlite_proto::KeyValueData<TrustTokenIssuerConfig>* data =
database_owner_->IssuerData();
for (const auto& kv : data->GetAllCached()) {
base::Optional<SuitableTrustTokenOrigin> origin =
SuitableTrustTokenOrigin::Create(GURL(kv.first));
// The Create call can fail when the SQLite data was corrupted on the disk.
if (origin) {
result.emplace(std::move(*origin), kv.second.tokens_size());
}
}
return result;
}
} // namespace network } // namespace network
...@@ -83,6 +83,9 @@ class SQLiteTrustTokenPersister : public TrustTokenPersister { ...@@ -83,6 +83,9 @@ class SQLiteTrustTokenPersister : public TrustTokenPersister {
base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)> matcher) base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)> matcher)
override; override;
base::flat_map<SuitableTrustTokenOrigin, int> GetStoredTrustTokenCounts()
override;
private: private:
// Manages the underlying database. // Manages the underlying database.
std::unique_ptr<TrustTokenDatabaseOwner> database_owner_; std::unique_ptr<TrustTokenDatabaseOwner> database_owner_;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h" #include "services/network/trust_tokens/suitable_trust_token_origin.h"
namespace network { namespace network {
...@@ -52,6 +53,9 @@ class TrustTokenPersister { ...@@ -52,6 +53,9 @@ class TrustTokenPersister {
virtual bool DeleteForOrigins( virtual bool DeleteForOrigins(
base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)> base::RepeatingCallback<bool(const SuitableTrustTokenOrigin&)>
matcher) = 0; matcher) = 0;
virtual base::flat_map<SuitableTrustTokenOrigin, int>
GetStoredTrustTokenCounts() = 0;
}; };
} // namespace network } // namespace network
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/test/bind.h" #include "base/test/bind.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/trust_tokens/in_memory_trust_token_persister.h" #include "services/network/trust_tokens/in_memory_trust_token_persister.h"
#include "services/network/trust_tokens/proto/public.pb.h" #include "services/network/trust_tokens/proto/public.pb.h"
#include "services/network/trust_tokens/proto/storage.pb.h" #include "services/network/trust_tokens/proto/storage.pb.h"
...@@ -343,4 +344,38 @@ TYPED_TEST(TrustTokenPersisterTest, DeletesToplevelKeyedData) { ...@@ -343,4 +344,38 @@ TYPED_TEST(TrustTokenPersisterTest, DeletesToplevelKeyedData) {
env.RunUntilIdle(); env.RunUntilIdle();
} }
TYPED_TEST(TrustTokenPersisterTest, RetrievesAvailableTrustTokens) {
base::test::TaskEnvironment env;
std::unique_ptr<TrustTokenPersister> persister = TypeParam::Create();
env.RunUntilIdle(); // Give implementations with asynchronous initialization
// time to initialize.
auto result = persister->GetStoredTrustTokenCounts();
EXPECT_EQ(result.size(), 0ul);
TrustTokenIssuerConfig config;
TrustToken my_token;
my_token.set_body("token token token");
*config.add_tokens() = my_token;
auto config_to_store = std::make_unique<TrustTokenIssuerConfig>(config);
auto origin = *SuitableTrustTokenOrigin::Create(GURL("https://a.com/"));
persister->SetIssuerConfig(origin, std::move(config_to_store));
env.RunUntilIdle(); // Give implementations with asynchronous write
// operations time to complete the operation.
result = persister->GetStoredTrustTokenCounts();
EXPECT_EQ(result.size(), 1ul);
EXPECT_EQ(result.begin()->first, origin);
EXPECT_EQ(result.begin()->second, 1);
// Some implementations of TrustTokenPersister may release resources
// asynchronously at destruction time; manually free the persister and allow
// this asynchronous release to occur, if any.
persister.reset();
env.RunUntilIdle();
}
} // namespace network } // namespace network
...@@ -311,4 +311,9 @@ bool TrustTokenStore::ClearDataForFilter(mojom::ClearDataFilterPtr filter) { ...@@ -311,4 +311,9 @@ bool TrustTokenStore::ClearDataForFilter(mojom::ClearDataFilterPtr filter) {
return persister_->DeleteForOrigins(std::move(matcher)); return persister_->DeleteForOrigins(std::move(matcher));
} }
base::flat_map<SuitableTrustTokenOrigin, int>
TrustTokenStore::GetStoredTrustTokenCounts() {
return persister_->GetStoredTrustTokenCounts();
}
} // namespace network } // namespace network
...@@ -153,6 +153,10 @@ class TrustTokenStore { ...@@ -153,6 +153,10 @@ class TrustTokenStore {
WARN_UNUSED_RESULT virtual int CountTokens( WARN_UNUSED_RESULT virtual int CountTokens(
const SuitableTrustTokenOrigin& issuer); const SuitableTrustTokenOrigin& issuer);
// Returns the number of stored tokens per issuer.
WARN_UNUSED_RESULT virtual base::flat_map<SuitableTrustTokenOrigin, int>
GetStoredTrustTokenCounts();
// Returns all signed tokens from |issuer| signed by keys matching // Returns all signed tokens from |issuer| signed by keys matching
// the given predicate. // the given predicate.
WARN_UNUSED_RESULT virtual std::vector<TrustToken> RetrieveMatchingTokens( WARN_UNUSED_RESULT virtual std::vector<TrustToken> RetrieveMatchingTokens(
......
...@@ -284,6 +284,32 @@ TEST(TrustTokenStore, CountsTokens) { ...@@ -284,6 +284,32 @@ TEST(TrustTokenStore, CountsTokens) {
EXPECT_EQ(my_store->CountTokens(issuer), 3); EXPECT_EQ(my_store->CountTokens(issuer), 3);
} }
TEST(TrustTokenStore, GetsAllStoredTokens) {
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
// A freshly initialized store should be storing zero tokens.
EXPECT_TRUE(my_store->GetStoredTrustTokenCounts().empty());
// Add a token; the count should increase.
SuitableTrustTokenOrigin issuer_a =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer-a.com"));
my_store->AddTokens(issuer_a, std::vector<std::string>(1),
/*issuing_key=*/"");
auto result = my_store->GetStoredTrustTokenCounts();
EXPECT_TRUE(result.contains(issuer_a));
EXPECT_EQ(result.find(issuer_a)->second, 1);
// Add two tokens for a different issuer.
SuitableTrustTokenOrigin issuer_b =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer-b.com"));
my_store->AddTokens(issuer_b, std::vector<std::string>(2),
/*issuing_key=*/"");
result = my_store->GetStoredTrustTokenCounts();
EXPECT_TRUE(result.contains(issuer_b));
EXPECT_EQ(result.find(issuer_b)->second, 2);
}
TEST(TrustTokenStore, PrunesDataAssociatedWithRemovedKeyCommitments) { TEST(TrustTokenStore, PrunesDataAssociatedWithRemovedKeyCommitments) {
// Test that providing PruneStaleIssuerState a set of key commitments // Test that providing PruneStaleIssuerState a set of key commitments
// correctly evicts all tokens except those associated with keys in the // correctly evicts all tokens except those associated with keys in the
......
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