blob: 5f5f231be282e7b6da6aa8a12d85e6d6c36fcc0e [file] [log] [blame]
// Copyright 2019 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 "services/network/trust_tokens/trust_token_store.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "services/network/public/mojom/network_context.mojom.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/storage.pb.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_parameterization.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
using ::testing::ElementsAre;
using ::testing::Optional;
namespace network {
namespace trust_tokens {
namespace {
MATCHER_P(EqualsProto,
message,
"Match a proto Message equal to the matcher's argument.") {
std::string expected_serialized, actual_serialized;
message.SerializeToString(&expected_serialized);
arg.SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
} // namespace
TEST(TrustTokenStoreTest, RecordsIssuances) {
// A newly initialized store should not think it's
// recorded any issuances.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
EXPECT_EQ(my_store->TimeSinceLastIssuance(issuer), base::nullopt);
// Recording an issuance should result in the time
// since last issuance being correctly returned.
my_store->RecordIssuance(issuer);
auto delta = base::TimeDelta::FromSeconds(1);
env.AdvanceClock(delta);
EXPECT_THAT(my_store->TimeSinceLastIssuance(issuer), Optional(delta));
}
TEST(TrustTokenStoreTest, DoesntReportMissingOrMalformedIssuanceTimestamps) {
auto my_persister = std::make_unique<InMemoryTrustTokenPersister>();
auto* raw_persister = my_persister.get();
auto my_store = TrustTokenStore::CreateForTesting(std::move(my_persister));
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto issuer_config_with_no_time = std::make_unique<TrustTokenIssuerConfig>();
raw_persister->SetIssuerConfig(issuer, std::move(issuer_config_with_no_time));
EXPECT_EQ(my_store->TimeSinceLastIssuance(issuer), base::nullopt);
auto issuer_config_with_malformed_time =
std::make_unique<TrustTokenIssuerConfig>();
issuer_config_with_malformed_time->set_last_issuance(
"not a valid serialization of a base::Time");
raw_persister->SetIssuerConfig(issuer,
std::move(issuer_config_with_malformed_time));
EXPECT_EQ(my_store->TimeSinceLastIssuance(issuer), base::nullopt);
}
TEST(TrustTokenStoreTest, DoesntReportNegativeTimeSinceLastIssuance) {
auto my_persister = std::make_unique<InMemoryTrustTokenPersister>();
auto* raw_persister = my_persister.get();
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
auto my_store = TrustTokenStore::CreateForTesting(std::move(my_persister));
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
base::Time later_than_now =
base::Time::Now() + base::TimeDelta::FromSeconds(1);
auto issuer_config_with_future_time =
std::make_unique<TrustTokenIssuerConfig>();
issuer_config_with_future_time->set_last_issuance(
internal::TimeToString(later_than_now));
raw_persister->SetIssuerConfig(issuer,
std::move(issuer_config_with_future_time));
// TimeSinceLastIssuance shouldn't return negative values.
EXPECT_EQ(my_store->TimeSinceLastIssuance(issuer), base::nullopt);
}
TEST(TrustTokenStore, RecordsRedemptions) {
// A newly initialized store should not think it's
// recorded any redemptions.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
EXPECT_EQ(my_store->TimeSinceLastRedemption(issuer, toplevel), base::nullopt);
// Recording a redemption should result in the time
// since last redemption being correctly returned.
my_store->RecordRedemption(issuer, toplevel);
auto delta = base::TimeDelta::FromSeconds(1);
env.AdvanceClock(delta);
EXPECT_THAT(my_store->TimeSinceLastRedemption(issuer, toplevel),
Optional(delta));
}
TEST(TrustTokenStoreTest, DoesntReportMissingOrMalformedRedemptionTimestamps) {
auto my_persister = std::make_unique<InMemoryTrustTokenPersister>();
auto* raw_persister = my_persister.get();
auto my_store = TrustTokenStore::CreateForTesting(std::move(my_persister));
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
auto config_with_no_time =
std::make_unique<TrustTokenIssuerToplevelPairConfig>();
raw_persister->SetIssuerToplevelPairConfig(issuer, toplevel,
std::move(config_with_no_time));
EXPECT_EQ(my_store->TimeSinceLastRedemption(issuer, toplevel), base::nullopt);
auto config_with_malformed_time =
std::make_unique<TrustTokenIssuerToplevelPairConfig>();
config_with_malformed_time->set_last_redemption(
"not a valid serialization of a base::Time");
raw_persister->SetIssuerToplevelPairConfig(
issuer, toplevel, std::move(config_with_malformed_time));
EXPECT_EQ(my_store->TimeSinceLastRedemption(issuer, toplevel), base::nullopt);
}
TEST(TrustTokenStoreTest, DoesntReportNegativeTimeSinceLastRedemption) {
auto my_persister = std::make_unique<InMemoryTrustTokenPersister>();
auto* raw_persister = my_persister.get();
auto my_store = TrustTokenStore::CreateForTesting(std::move(my_persister));
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
base::Time later_than_now =
base::Time::Now() + base::TimeDelta::FromSeconds(1);
auto config_with_future_time =
std::make_unique<TrustTokenIssuerToplevelPairConfig>();
config_with_future_time->set_last_redemption(
internal::TimeToString(later_than_now));
raw_persister->SetIssuerToplevelPairConfig(
issuer, toplevel, std::move(config_with_future_time));
// TimeSinceLastRedemption shouldn't return negative values.
EXPECT_EQ(my_store->TimeSinceLastRedemption(issuer, toplevel), base::nullopt);
}
TEST(TrustTokenStore, AssociatesToplevelsWithIssuers) {
// A newly initialized store should not think
// any toplevels are associated with any issuers.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
EXPECT_FALSE(my_store->IsAssociated(issuer, toplevel));
// After associating an issuer with a toplevel,
// the store should think that that issuer is associated
// with that toplevel.
EXPECT_TRUE(my_store->SetAssociation(issuer, toplevel));
EXPECT_TRUE(my_store->IsAssociated(issuer, toplevel));
}
// Test that issuer-toplevel association works correctly when a toplevel's
// number-of-issuance cap has been reached: reassocating an already-associated
// issuer should succeed, while associating any other issuer should fail.
TEST(TrustTokenStore, IssuerToplevelAssociationAtNumberOfAssociationsCap) {
auto persister = std::make_unique<InMemoryTrustTokenPersister>();
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto config = std::make_unique<TrustTokenToplevelConfig>();
for (int i = 0; i < kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers - 1;
++i)
config->add_associated_issuers();
*config->add_associated_issuers() = issuer.Serialize();
persister->SetToplevelConfig(toplevel, std::move(config));
auto my_store = TrustTokenStore::CreateForTesting(std::move(persister));
// Sanity check that the test set the config up correctly.
ASSERT_TRUE(my_store->IsAssociated(issuer, toplevel));
// Even though we're at the cap, SetAssociation for an already-associated
// toplevel should return true.
EXPECT_TRUE(my_store->SetAssociation(issuer, toplevel));
// Since we're at the cap, SetAssociation for an issuer not already associated
// with the top-level origin should fail.
EXPECT_FALSE(my_store->SetAssociation(
*SuitableTrustTokenOrigin::Create(GURL("https://someotherissuer.com")),
toplevel));
}
TEST(TrustTokenStore, AddingTokensRespectsCapacity) {
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
// Attempting to add many, many tokens corresponding to that key should be
// successful, but the operation should only add a quantity of tokens equal to
// the difference between the number of currently-stored tokens and the
// capacity.
my_store->AddTokens(
issuer, std::vector<std::string>(kTrustTokenPerIssuerTokenCapacity * 2),
/*issuing_key=*/
"");
EXPECT_EQ(my_store->CountTokens(issuer), kTrustTokenPerIssuerTokenCapacity);
}
TEST(TrustTokenStore, CountsTokens) {
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
// A freshly initialized store should be storing zero tokens.
EXPECT_EQ(my_store->CountTokens(issuer), 0);
// Add a token; the count should increase.
my_store->AddTokens(issuer, std::vector<std::string>(1),
/*issuing_key=*/"");
EXPECT_EQ(my_store->CountTokens(issuer), 1);
// Add two more tokens; the count should change accordingly.
my_store->AddTokens(issuer, std::vector<std::string>(2),
/*issuing_key=*/"");
EXPECT_EQ(my_store->CountTokens(issuer), 3);
}
TEST(TrustTokenStore, PrunesDataAssociatedWithRemovedKeyCommitments) {
// Test that providing PruneStaleIssuerState a set of key commitments
// correctly evicts all tokens except those associated with keys in the
// provided set of commitments.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
my_store->AddTokens(issuer, std::vector<std::string>{"some token body"},
"quite a secure key, this");
auto another_commitment = mojom::TrustTokenVerificationKey::New();
another_commitment->body = "distinct from the first key";
my_store->AddTokens(issuer, std::vector<std::string>{"some other token body"},
another_commitment->body);
// The prune should remove the first token added, because it corresponds to a
// key not in the list of commitments provided to PruneStaleIssuerState.
std::vector<mojom::TrustTokenVerificationKeyPtr> keys;
keys.emplace_back(another_commitment.Clone());
my_store->PruneStaleIssuerState(issuer, keys);
TrustToken expected_token;
expected_token.set_body("some other token body");
expected_token.set_signing_key(another_commitment->body);
// Removing |my_commitment| should have
// - led to the removal of the token associated with the removed key and
// - *not* led to the removal of the token associated with the remaining key.
EXPECT_THAT(my_store->RetrieveMatchingTokens(
issuer, base::BindRepeating(
[](const std::string& t) { return true; })),
ElementsAre(EqualsProto(expected_token)));
}
TEST(TrustTokenStore, AddsTrustTokens) {
// A newly initialized store should not think
// any issuers have associated trust tokens.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto match_all_keys =
base::BindRepeating([](const std::string& t) { return true; });
EXPECT_TRUE(my_store->RetrieveMatchingTokens(issuer, match_all_keys).empty());
// Adding a token should result in that token being
// returned by subsequent queries with predicates accepting
// that token.
const std::string kMyKey = "abcdef";
auto my_commitment = mojom::TrustTokenVerificationKey::New();
my_commitment->body = kMyKey;
TrustToken expected_token;
expected_token.set_body("some token");
expected_token.set_signing_key(kMyKey);
my_store->AddTokens(issuer, std::vector<std::string>{expected_token.body()},
kMyKey);
EXPECT_THAT(my_store->RetrieveMatchingTokens(issuer, match_all_keys),
ElementsAre(EqualsProto(expected_token)));
}
TEST(TrustTokenStore, RetrievesTrustTokensRespectingNontrivialPredicate) {
// RetrieveMatchingTokens should not return tokens rejected by
// the provided predicate.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
const std::string kMatchingKey = "bbbbbb";
const std::string kNonmatchingKey = "aaaaaa";
auto matching_commitment = mojom::TrustTokenVerificationKey::New();
matching_commitment->body = kMatchingKey;
auto nonmatching_commitment = mojom::TrustTokenVerificationKey::New();
nonmatching_commitment->body = kNonmatchingKey;
TrustToken expected_token;
expected_token.set_body("this one should get returned");
expected_token.set_signing_key(kMatchingKey);
my_store->AddTokens(issuer, std::vector<std::string>{expected_token.body()},
kMatchingKey);
my_store->AddTokens(
issuer,
std::vector<std::string>{"this one should get rejected by the predicate"},
kNonmatchingKey);
EXPECT_THAT(my_store->RetrieveMatchingTokens(
issuer, base::BindRepeating(
[](const std::string& pattern,
const std::string& possible_match) {
return possible_match == pattern;
},
kMatchingKey)),
ElementsAre(EqualsProto(expected_token)));
}
TEST(TrustTokenStore, DeletesSingleToken) {
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto match_all_keys =
base::BindRepeating([](const std::string& t) { return true; });
// Deleting a single token should result in that token
// not being returned by subsequent RetrieveMatchingTokens calls.
// On the other hand, tokens *not* deleted should still be
// returned.
auto my_commitment = mojom::TrustTokenVerificationKey::New();
my_commitment->body = "key";
TrustToken first_token;
first_token.set_body("delete me!");
first_token.set_signing_key(my_commitment->body);
TrustToken second_token;
second_token.set_body("don't delete me!");
second_token.set_signing_key(my_commitment->body);
my_store->AddTokens(
issuer, std::vector<std::string>{first_token.body(), second_token.body()},
my_commitment->body);
my_store->DeleteToken(issuer, first_token);
EXPECT_THAT(my_store->RetrieveMatchingTokens(issuer, match_all_keys),
ElementsAre(EqualsProto(second_token)));
}
TEST(TrustTokenStore, DeleteTokenForMissingIssuer) {
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
// Deletes for issuers not present in the store should gracefully no-op.
my_store->DeleteToken(issuer, TrustToken());
}
TEST(TrustTokenStore, SetsAndRetrievesRedemptionRecord) {
// A newly initialized store should not think
// it has any signed redemption records.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
EXPECT_EQ(my_store->RetrieveNonstaleRedemptionRecord(issuer, toplevel),
base::nullopt);
// Providing a redemption record should mean that subsequent
// queries (modulo the record's staleness) should return that
// record.
SignedTrustTokenRedemptionRecord my_record;
my_record.set_body("Look at me! I'm a signed redemption record!");
my_store->SetRedemptionRecord(issuer, toplevel, my_record);
EXPECT_THAT(my_store->RetrieveNonstaleRedemptionRecord(issuer, toplevel),
Optional(EqualsProto(my_record)));
}
TEST(TrustTokenStore, RetrieveRedemptionRecordHandlesConfigWithNoRecord) {
// A RetrieveRedemptionRecord call for an (issuer, toplevel) pair with
// no redemption record stored should gracefully return the default value.
auto my_persister = std::make_unique<InMemoryTrustTokenPersister>();
auto* raw_persister = my_persister.get();
auto my_store = TrustTokenStore::CreateForTesting(std::move(my_persister));
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
raw_persister->SetIssuerToplevelPairConfig(
issuer, toplevel, std::make_unique<TrustTokenIssuerToplevelPairConfig>());
EXPECT_EQ(my_store->RetrieveNonstaleRedemptionRecord(issuer, toplevel),
base::nullopt);
}
TEST(TrustTokenStore, SetRedemptionRecordOverwritesExisting) {
// Subsequent redemption records should overwrite ones set earlier.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
SignedTrustTokenRedemptionRecord my_record;
my_record.set_body("Look at me! I'm a signed redemption record!");
my_store->SetRedemptionRecord(issuer, toplevel, my_record);
SignedTrustTokenRedemptionRecord another_record;
another_record.set_body(
"If all goes well, this one should overwrite |my_record|.");
my_store->SetRedemptionRecord(issuer, toplevel, another_record);
EXPECT_THAT(my_store->RetrieveNonstaleRedemptionRecord(issuer, toplevel),
Optional(EqualsProto(another_record)));
}
namespace {
// Characterizes an SRR as expired if its body begins with an "a".
class LetterAExpiringExpiryDelegate
: public TrustTokenStore::RecordExpiryDelegate {
public:
bool IsRecordExpired(const SignedTrustTokenRedemptionRecord& record,
const SuitableTrustTokenOrigin&) override {
return record.body().size() > 1 && record.body().front() == 'a';
}
};
} // namespace
TEST(TrustTokenStore, DoesNotReturnStaleRedemptionRecord) {
// Once a redemption record expires, it should no longer
// be returned by retrieval queries.
auto my_store = TrustTokenStore::CreateForTesting(
std::make_unique<InMemoryTrustTokenPersister>(),
std::make_unique<LetterAExpiringExpiryDelegate>());
SuitableTrustTokenOrigin issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
SuitableTrustTokenOrigin toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
SignedTrustTokenRedemptionRecord my_record;
my_record.set_body("aLook at me! I'm an expired signed redemption record!");
my_store->SetRedemptionRecord(issuer, toplevel, my_record);
EXPECT_EQ(my_store->RetrieveNonstaleRedemptionRecord(issuer, toplevel),
base::nullopt);
}
TEST(TrustTokenStore, EmptyFilter) {
// Deletion with an empty filter should no-op.
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
EXPECT_FALSE(store->ClearDataForFilter(mojom::ClearDataFilter::New()));
EXPECT_TRUE(store->CountTokens(issuer));
}
TEST(TrustTokenStore, EmptyFilterInKeepMatchesMode) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
// An empty filter in mode KEEP_MATCHES should act as a wildcard "wipe the
// store" clear.
auto filter_matching_all_data = mojom::ClearDataFilter::New();
filter_matching_all_data->type = mojom::ClearDataFilter::Type::KEEP_MATCHES;
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter_matching_all_data)));
EXPECT_FALSE(store->CountTokens(issuer));
}
TEST(TrustTokenStore, ClearsIssuerKeyedData) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
auto filter = mojom::ClearDataFilter::New();
filter->origins.push_back(issuer);
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_FALSE(store->CountTokens(issuer));
}
TEST(TrustTokenStore, ClearsToplevelKeyedData) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
// (The list of issuers associated with each top-level origin is keyed solely
// by top-level origin because it is configuration set by the top-level
// origin.)
ASSERT_TRUE(store->SetAssociation(issuer, toplevel));
auto filter = mojom::ClearDataFilter::New();
filter->origins.push_back(toplevel);
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_FALSE(store->IsAssociated(issuer, toplevel));
}
TEST(TrustTokenStore, ClearsIssuerToplevelPairKeyedData) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com"));
auto toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"));
{
store->SetRedemptionRecord(issuer, toplevel,
SignedTrustTokenRedemptionRecord());
auto filter = mojom::ClearDataFilter::New();
filter->origins.push_back(issuer);
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel));
}
{
store->SetRedemptionRecord(issuer, toplevel,
SignedTrustTokenRedemptionRecord());
auto filter = mojom::ClearDataFilter::New();
filter->origins.push_back(toplevel);
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel));
}
}
TEST(TrustTokenStore, ClearDataCanFilterByDomain) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer = *SuitableTrustTokenOrigin::Create(
GURL("https://arbitrary.https.subdomain.of.issuer.com"));
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
auto filter = mojom::ClearDataFilter::New();
filter->domains.push_back("issuer.com");
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_FALSE(store->CountTokens(issuer));
}
TEST(TrustTokenStore, RemovesDataForInvertedFilters) {
auto store = TrustTokenStore::CreateForTesting();
auto issuer =
*SuitableTrustTokenOrigin::Create(GURL("https://www.issuer.com"));
auto toplevel =
*SuitableTrustTokenOrigin::Create(GURL("https://www.toplevel.com"));
store->AddTokens(issuer, std::vector<std::string>{"token"}, "key");
store->SetRedemptionRecord(issuer, toplevel,
SignedTrustTokenRedemptionRecord{});
// With a "delete all origins not covered by this filter"-type filter
// containing just the issuer, the issuer's data shouldn't be touched, but the
// (issuer, toplevel)-keyed data should be cleared as one origin (the
// non-matching one) qualifies for deletion.
auto filter = mojom::ClearDataFilter::New();
filter->type = mojom::ClearDataFilter::Type::KEEP_MATCHES;
filter->origins.push_back(issuer);
EXPECT_TRUE(store->ClearDataForFilter(std::move(filter)));
EXPECT_TRUE(store->CountTokens(issuer));
EXPECT_FALSE(store->RetrieveNonstaleRedemptionRecord(issuer, toplevel));
}
TEST(TrustTokenStore, RemovesDataForNullFilter) {
// A null filter is a "clear all data" wildcard.
auto store = TrustTokenStore::CreateForTesting();
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