blob: 7711d8fa9ef67f79f79573ef114c1c469cb89734 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/attribution_reporting/destination_set.h"
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/ranges/algorithm.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "components/attribution_reporting/constants.h"
#include "components/attribution_reporting/source_registration_error.mojom-shared.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace attribution_reporting {
namespace {
using ::attribution_reporting::mojom::SourceRegistrationError;
absl::optional<net::SchemefulSite> DeserializeDestination(
const std::string& s) {
auto destination = SuitableOrigin::Deserialize(s);
if (!destination.has_value()) {
return absl::nullopt;
}
return net::SchemefulSite(*destination);
}
bool DestinationsValid(const DestinationSet::Destinations& destinations) {
return !destinations.empty() && destinations.size() <= kMaxDestinations &&
base::ranges::all_of(destinations, &IsSitePotentiallySuitable);
}
} // namespace
// static
absl::optional<DestinationSet> DestinationSet::Create(
Destinations destinations) {
if (!DestinationsValid(destinations)) {
return absl::nullopt;
}
return DestinationSet(std::move(destinations));
}
// static
base::expected<DestinationSet, SourceRegistrationError>
DestinationSet::FromJSON(const base::Value* v) {
if (!v) {
return base::unexpected(SourceRegistrationError::kDestinationMissing);
}
std::vector<net::SchemefulSite> destination_sites;
if (const std::string* str = v->GetIfString()) {
auto destination = DeserializeDestination(*str);
if (!destination.has_value()) {
return base::unexpected(
SourceRegistrationError::kDestinationUntrustworthy);
}
destination_sites.push_back(std::move(*destination));
} else if (const base::Value::List* list = v->GetIfList()) {
if (list->size() > kMaxDestinations) {
return base::unexpected(SourceRegistrationError::kDestinationListTooLong);
}
if (list->empty()) {
return base::unexpected(SourceRegistrationError::kDestinationMissing);
}
destination_sites.reserve(list->size());
for (const auto& item : *list) {
const std::string* item_str = item.GetIfString();
if (!item_str) {
return base::unexpected(SourceRegistrationError::kDestinationWrongType);
}
auto destination = DeserializeDestination(*item_str);
if (!destination.has_value()) {
return base::unexpected(
SourceRegistrationError::kDestinationUntrustworthy);
}
destination_sites.push_back(std::move(*destination));
}
} else {
return base::unexpected(SourceRegistrationError::kDestinationWrongType);
}
return DestinationSet(std::move(destination_sites));
}
DestinationSet::DestinationSet(Destinations destinations)
: destinations_(std::move(destinations)) {
DCHECK(DestinationsValid(destinations_));
}
DestinationSet::DestinationSet() = default;
DestinationSet::~DestinationSet() = default;
DestinationSet::DestinationSet(const DestinationSet&) = default;
DestinationSet::DestinationSet(DestinationSet&&) = default;
DestinationSet& DestinationSet::operator=(const DestinationSet&) = default;
DestinationSet& DestinationSet::operator=(DestinationSet&&) = default;
bool DestinationSet::IsValid() const {
return DestinationsValid(destinations_);
}
base::Value DestinationSet::ToJson() const {
DCHECK(IsValid());
if (destinations_.size() == 1) {
return base::Value(destinations_.begin()->Serialize());
}
base::Value::List list;
for (const auto& destination : destinations_) {
list.Append(destination.Serialize());
}
return base::Value(std::move(list));
}
} // namespace attribution_reporting