Commit 98457f89 authored by Leonid Baraz's avatar Leonid Baraz Committed by Chromium LUCI CQ

Add Upload request Value builders.

Bug: 1078512
Change-Id: I8809b5c6b4a55f3440d20830e456cec8063f7190
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2578017
Commit-Queue: Leonid Baraz <lbaraz@chromium.org>
Commit-Queue: Zach Trudo <zatrudo@google.com>
Reviewed-by: default avatarZach Trudo <zatrudo@google.com>
Auto-Submit: Leonid Baraz <lbaraz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834533}
parent b513198e
...@@ -1234,6 +1234,8 @@ static_library("browser") { ...@@ -1234,6 +1234,8 @@ static_library("browser") {
"policy/messaging_layer/upload/dm_server_upload_service.h", "policy/messaging_layer/upload/dm_server_upload_service.h",
"policy/messaging_layer/upload/record_handler_impl.cc", "policy/messaging_layer/upload/record_handler_impl.cc",
"policy/messaging_layer/upload/record_handler_impl.h", "policy/messaging_layer/upload/record_handler_impl.h",
"policy/messaging_layer/upload/record_upload_request_builder.cc",
"policy/messaging_layer/upload/record_upload_request_builder.h",
"policy/messaging_layer/upload/upload_client.cc", "policy/messaging_layer/upload/upload_client.cc",
"policy/messaging_layer/upload/upload_client.h", "policy/messaging_layer/upload/upload_client.h",
"policy/messaging_layer/util/backoff_settings.cc", "policy/messaging_layer/util/backoff_settings.cc",
......
// Copyright 2020 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 "chrome/browser/policy/messaging_layer/upload/record_upload_request_builder.h"
#include <string>
#include "base/base64.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "components/policy/proto/record.pb.h"
#include "components/policy/proto/record_constants.pb.h"
namespace reporting {
namespace {
// UploadEncryptedReportingRequestBuilder list key
constexpr char kEncryptedRecordListKey[] = "encryptedRecord";
// EncrypedRecordDictionaryBuilder strings
constexpr char kEncryptedWrappedRecord[] = "encryptedWrappedRecord";
constexpr char kSequencingInformationKey[] = "sequencingInformation";
constexpr char kEncryptionInfoKey[] = "encryptionInfo";
// SequencingInformationDictionaryBuilder strings
constexpr char kSequencingId[] = "sequencingId";
constexpr char kGenerationId[] = "generationId";
constexpr char kPriority[] = "priority";
// EncryptionInfoDictionaryBuilder strings
constexpr char kEncryptionKey[] = "encryptionKey";
constexpr char kPublicKeyId[] = "publicKeyId";
} // namespace
UploadEncryptedReportingRequestBuilder::
UploadEncryptedReportingRequestBuilder() {
result_ = base::Value{base::Value::Type::DICTIONARY};
result_.value().SetKey(GetEncryptedRecordListPath(),
base::Value{base::Value::Type::LIST});
}
UploadEncryptedReportingRequestBuilder::
~UploadEncryptedReportingRequestBuilder() = default;
UploadEncryptedReportingRequestBuilder&
UploadEncryptedReportingRequestBuilder::AddRecord(
const EncryptedRecord& record) {
if (!result_.has_value()) {
// Some errors were already detected.
return *this;
}
base::Value* const records_list =
result_.value().FindListKey(GetEncryptedRecordListPath());
if (!records_list || !records_list->is_list()) {
NOTREACHED(); // Should not happen.
return *this;
}
auto record_result = EncryptedRecordDictionaryBuilder(record).Build();
if (!record_result.has_value()) {
// Record has errors. Stop here.
result_ = base::nullopt;
return *this;
}
records_list->Append(std::move(record_result.value()));
return *this;
}
base::Optional<base::Value> UploadEncryptedReportingRequestBuilder::Build() {
return std::move(result_);
}
// static
base::StringPiece
UploadEncryptedReportingRequestBuilder::GetEncryptedRecordListPath() {
return kEncryptedRecordListKey;
}
EncryptedRecordDictionaryBuilder::EncryptedRecordDictionaryBuilder(
const EncryptedRecord& record) {
base::Value record_dictionary{base::Value::Type::DICTIONARY};
// A record without sequencing information cannot be uploaded - deny it.
if (!record.has_sequencing_information()) {
return;
}
auto sequencing_information_result =
SequencingInformationDictionaryBuilder(record.sequencing_information())
.Build();
if (!sequencing_information_result.has_value()) {
// Sequencing information was improperly configured. Record cannot be
// uploaded. Deny it.
return;
}
record_dictionary.SetKey(GetSequencingInformationKeyPath(),
std::move(sequencing_information_result.value()));
// Encryption information can be missing until we set up encryption as
// mandatory.
if (record.has_encryption_info()) {
auto encryption_info_result =
EncryptionInfoDictionaryBuilder(record.encryption_info()).Build();
if (!encryption_info_result.has_value()) {
// Encryption info has been corrupted or set improperly. Deny it.
return;
}
record_dictionary.SetKey(GetEncryptionInfoPath(),
std::move(encryption_info_result.value()));
}
// Gap records won't fill in this field, so it can be missing.
if (record.has_encrypted_wrapped_record()) {
std::string base64_encode;
base::Base64Encode(record.encrypted_wrapped_record(), &base64_encode);
record_dictionary.SetStringKey(GetEncryptedWrappedRecordPath(),
base64_encode);
}
// Result complete.
result_ = std::move(record_dictionary);
}
EncryptedRecordDictionaryBuilder::~EncryptedRecordDictionaryBuilder() = default;
base::Optional<base::Value> EncryptedRecordDictionaryBuilder::Build() {
return std::move(result_);
}
// static
base::StringPiece
EncryptedRecordDictionaryBuilder::GetEncryptedWrappedRecordPath() {
return kEncryptedWrappedRecord;
}
// static
base::StringPiece
EncryptedRecordDictionaryBuilder::GetSequencingInformationKeyPath() {
return kSequencingInformationKey;
}
// static
base::StringPiece EncryptedRecordDictionaryBuilder::GetEncryptionInfoPath() {
return kEncryptionInfoKey;
}
SequencingInformationDictionaryBuilder::SequencingInformationDictionaryBuilder(
const SequencingInformation& sequencing_information) {
// SequencingInformation requires all three fields be set.
if (!sequencing_information.has_sequencing_id() ||
!sequencing_information.has_generation_id() ||
!sequencing_information.has_priority()) {
return;
}
base::Value sequencing_dictionary{base::Value::Type::DICTIONARY};
sequencing_dictionary.SetStringKey(
GetSequencingIdPath(),
base::NumberToString(sequencing_information.sequencing_id()));
sequencing_dictionary.SetStringKey(
GetGenerationIdPath(),
base::NumberToString(sequencing_information.generation_id()));
sequencing_dictionary.SetIntKey(GetPriorityPath(),
sequencing_information.priority());
result_ = std::move(sequencing_dictionary);
}
SequencingInformationDictionaryBuilder::
~SequencingInformationDictionaryBuilder() = default;
base::Optional<base::Value> SequencingInformationDictionaryBuilder::Build() {
return std::move(result_);
}
// static
base::StringPiece
SequencingInformationDictionaryBuilder::GetSequencingIdPath() {
return kSequencingId;
}
// static
base::StringPiece
SequencingInformationDictionaryBuilder::GetGenerationIdPath() {
return kGenerationId;
}
// static
base::StringPiece SequencingInformationDictionaryBuilder::GetPriorityPath() {
return kPriority;
}
EncryptionInfoDictionaryBuilder::EncryptionInfoDictionaryBuilder(
const EncryptionInfo& encryption_info) {
base::Value encryption_info_dictionary{base::Value::Type::DICTIONARY};
// EncryptionInfo requires both fields are set.
if (!encryption_info.has_encryption_key() ||
!encryption_info.has_public_key_id()) {
return;
}
encryption_info_dictionary.SetStringKey(GetEncryptionKeyPath(),
encryption_info.encryption_key());
encryption_info_dictionary.SetStringKey(
GetPublicKeyIdPath(),
base::NumberToString(encryption_info.public_key_id()));
result_ = std::move(encryption_info_dictionary);
}
EncryptionInfoDictionaryBuilder::~EncryptionInfoDictionaryBuilder() = default;
base::Optional<base::Value> EncryptionInfoDictionaryBuilder::Build() {
return std::move(result_);
}
// static
base::StringPiece EncryptionInfoDictionaryBuilder::GetEncryptionKeyPath() {
return kEncryptionKey;
}
// static
base::StringPiece EncryptionInfoDictionaryBuilder::GetPublicKeyIdPath() {
return kPublicKeyId;
}
} // namespace reporting
// Copyright 2020 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.
#ifndef CHROME_BROWSER_POLICY_MESSAGING_LAYER_UPLOAD_RECORD_UPLOAD_REQUEST_BUILDER_H_
#define CHROME_BROWSER_POLICY_MESSAGING_LAYER_UPLOAD_RECORD_UPLOAD_REQUEST_BUILDER_H_
#include <string>
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "components/policy/proto/record.pb.h"
namespace reporting {
// Builds an upload request payload specific for
// EncryptedReportingJobConfiguration A JSON version of the payload looks like
// this:
// {
// "encryptedRecord": [
// {
// "encryptedWrappedRecord": "EncryptedMessage",
// "encryptionInfo" : {
// "encryptionKey": "EncryptedMessage",
// "publicKeyId": 1
// },
// "sequencingInformation": {
// "sequencingId": 1,
// "generationId": 123456789,
// "priority": 1
// }
// },
// {
// "encryptedWrappedRecord": "EncryptedMessage",
// "encryptionInfo" : {
// "encryptionKey": "EncryptedMessage",
// "publicKeyId": 2
// },
// "sequencingInformation": {
// "sequencingId": 2,
// "generationId": 123456789,
// "priority": 1
// }
// }
// ]
// }
// TODO(b/170054326): Add 'need_encryption_key' field, if requested.
// TODO(b/159361496): Periodically add memory and disk space usage.
//
// This payload is added to the common payload of all reporting jobs, which
// includes "device" and "browser" sub-fields:
//
// EncryptedReportingRequestBuilder builder;
// builder.AddRecord(record1);
// builder.AddRecord(record2);
// ...
// builder.AddRecord(recordN);
// auto payload_result = builder.Build();
// DCHECK(payload_result.has_value());
// job_payload_.MergeDict(payload_result.value());
class UploadEncryptedReportingRequestBuilder {
public:
UploadEncryptedReportingRequestBuilder();
~UploadEncryptedReportingRequestBuilder();
UploadEncryptedReportingRequestBuilder& AddRecord(
const EncryptedRecord& record);
base::Optional<base::Value> Build();
static base::StringPiece GetEncryptedRecordListPath();
static const char kEncryptedRecordListKey_[];
base::Optional<base::Value> result_;
};
// Builds a |base::Value| dictionary from a |EncryptedRecord|
// proto.
class EncryptedRecordDictionaryBuilder {
public:
explicit EncryptedRecordDictionaryBuilder(const EncryptedRecord& record);
~EncryptedRecordDictionaryBuilder();
base::Optional<base::Value> Build();
static base::StringPiece GetEncryptedWrappedRecordPath();
static base::StringPiece GetSequencingInformationKeyPath();
static base::StringPiece GetEncryptionInfoPath();
private:
base::Optional<base::Value> result_;
};
// Builds a |base::Value| dictionary from a |SequencingInformation|
// proto.
class SequencingInformationDictionaryBuilder {
public:
explicit SequencingInformationDictionaryBuilder(
const SequencingInformation& sequencing_information);
~SequencingInformationDictionaryBuilder();
base::Optional<base::Value> Build();
static base::StringPiece GetSequencingIdPath();
static base::StringPiece GetGenerationIdPath();
static base::StringPiece GetPriorityPath();
private:
base::Optional<base::Value> result_;
};
// Builds a |base::Value| dictionary from a |EncryptionInfo| proto.
class EncryptionInfoDictionaryBuilder {
public:
explicit EncryptionInfoDictionaryBuilder(
const EncryptionInfo& encryption_info);
~EncryptionInfoDictionaryBuilder();
base::Optional<base::Value> Build();
static base::StringPiece GetEncryptionKeyPath();
static base::StringPiece GetPublicKeyIdPath();
private:
base::Optional<base::Value> result_;
};
} // namespace reporting
#endif // CHROME_BROWSER_POLICY_MESSAGING_LAYER_UPLOAD_RECORD_UPLOAD_REQUEST_BUILDER_H_
// Copyright 2020 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 "chrome/browser/policy/messaging_layer/upload/record_upload_request_builder.h"
#include <cstdint>
#include <string>
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Not;
namespace reporting {
namespace {
constexpr uint64_t kGenerationId = 4321;
constexpr Priority kPriority = Priority::IMMEDIATE;
// Default values for EncryptionInfo
constexpr char kEncryptionKey[] = "abcdef";
constexpr uint64_t kPublicKeyId = 9876;
class RecordUploadRequestBuilderTest : public ::testing::Test {
public:
RecordUploadRequestBuilderTest() = default;
protected:
static EncryptedRecord GenerateEncryptedRecord(
const base::StringPiece encrypted_wrapped_record) {
EncryptedRecord record;
record.set_encrypted_wrapped_record(std::string(encrypted_wrapped_record));
auto* const sequencing_information =
record.mutable_sequencing_information();
sequencing_information->set_sequencing_id(GetNextSequenceId());
sequencing_information->set_generation_id(kGenerationId);
sequencing_information->set_priority(kPriority);
auto* const encryption_info = record.mutable_encryption_info();
encryption_info->set_encryption_key(kEncryptionKey);
encryption_info->set_public_key_id(kPublicKeyId);
return record;
}
static uint64_t GetNextSequenceId() {
static uint64_t sequencing_id = 0;
return sequencing_id++;
}
};
TEST_F(RecordUploadRequestBuilderTest, AcceptEncryptedRecordsList) {
const std::vector<std::string> kEncryptedWrappedRecords{
"T", "E", "S", "T", "_", "I", "N", "F", "O"};
static constexpr size_t kNumRecords = 10;
std::vector<EncryptedRecord> records;
records.reserve(kNumRecords);
for (size_t counter = 0; counter < kNumRecords; ++counter) {
records.push_back(GenerateEncryptedRecord(
base::StrCat({"TEST_INFO_", base::NumberToString(counter)})));
}
UploadEncryptedReportingRequestBuilder builder;
for (const auto& record : records) {
builder.AddRecord(record);
}
auto request_payload = builder.Build();
ASSERT_TRUE(request_payload.has_value());
ASSERT_TRUE(request_payload.value().is_dict());
base::Value* const record_list = request_payload.value().FindListKey(
UploadEncryptedReportingRequestBuilder::GetEncryptedRecordListPath());
ASSERT_TRUE(record_list);
ASSERT_TRUE(record_list->is_list());
EXPECT_EQ(record_list->GetList().size(), records.size());
size_t counter = 0;
for (const auto& record : records) {
auto record_value_result = EncryptedRecordDictionaryBuilder(record).Build();
ASSERT_TRUE(record_value_result.has_value());
EXPECT_EQ(record_list->GetList()[counter++], record_value_result.value());
}
}
TEST_F(RecordUploadRequestBuilderTest, BreakListOnSingleBadRecord) {
const std::vector<std::string> kEncryptedWrappedRecords{
"T", "E", "S", "T", "_", "I", "N", "F", "O"};
static constexpr size_t kNumRecords = 10;
std::vector<EncryptedRecord> records;
records.reserve(kNumRecords);
for (size_t counter = 0; counter < kNumRecords; ++counter) {
records.push_back(GenerateEncryptedRecord(
base::StrCat({"TEST_INFO_", base::NumberToString(counter)})));
}
// Corrupt one record.
records[kNumRecords - 2]
.mutable_sequencing_information()
->clear_generation_id();
UploadEncryptedReportingRequestBuilder builder;
for (const auto& record : records) {
builder.AddRecord(record);
}
auto request_payload = builder.Build();
ASSERT_FALSE(request_payload.has_value()) << request_payload.value();
}
TEST_F(RecordUploadRequestBuilderTest, DenyPoorlyFormedEncryptedRecords) {
// Reject empty record.
EncryptedRecord record;
EXPECT_FALSE(EncryptedRecordDictionaryBuilder(record).Build().has_value());
// Reject encrypted_wrapped_record without sequencing information.
record.set_encrypted_wrapped_record("Enterprise");
EXPECT_FALSE(EncryptedRecordDictionaryBuilder(record).Build().has_value());
// Reject incorrectly set sequencing information by only setting sequencing
// id.
auto* sequencing_information = record.mutable_sequencing_information();
sequencing_information->set_sequencing_id(1701);
EXPECT_FALSE(EncryptedRecordDictionaryBuilder(record).Build().has_value());
// Finish correctly setting sequencing information but incorrectly set
// encryption info.
sequencing_information->set_generation_id(12345678);
sequencing_information->set_priority(IMMEDIATE);
auto* encryption_info = record.mutable_encryption_info();
encryption_info->set_encryption_key("Key");
EXPECT_FALSE(EncryptedRecordDictionaryBuilder(record).Build().has_value());
// Finish correctly setting encryption info - expect complete call.
encryption_info->set_public_key_id(1234);
EXPECT_TRUE(EncryptedRecordDictionaryBuilder(record).Build().has_value());
}
} // namespace
} // namespace reporting
...@@ -3636,6 +3636,7 @@ test("unit_tests") { ...@@ -3636,6 +3636,7 @@ test("unit_tests") {
"../browser/policy/messaging_layer/storage/test_storage_module.h", "../browser/policy/messaging_layer/storage/test_storage_module.h",
"../browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc", "../browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc",
"../browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc", "../browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc",
"../browser/policy/messaging_layer/upload/record_upload_request_builder_unittest.cc",
"../browser/policy/messaging_layer/upload/upload_client_unittest.cc", "../browser/policy/messaging_layer/upload/upload_client_unittest.cc",
"../browser/policy/messaging_layer/util/shared_queue_unittest.cc", "../browser/policy/messaging_layer/util/shared_queue_unittest.cc",
"../browser/policy/messaging_layer/util/shared_vector_unittest.cc", "../browser/policy/messaging_layer/util/shared_vector_unittest.cc",
......
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