blob: 2d853e1339ce7100e3e8f3289a72f75609358612 [file] [log] [blame]
// Copyright 2021 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/fake_upload_client.h"
#include <utility>
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/memory/ptr_util.h"
#include "base/strings/strcat.h"
#include "chrome/browser/policy/messaging_layer/upload/record_upload_request_builder.h"
#include "components/reporting/proto/record.pb.h"
#include "components/reporting/proto/record_constants.pb.h"
#include "components/reporting/util/status.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace reporting {
namespace {
absl::optional<Priority> GetPriorityFromSequencingInformationValue(
const base::Value& sequencing_information) {
const absl::optional<int> priority_result =
sequencing_information.FindIntKey("priority");
if (!priority_result.has_value() ||
!Priority_IsValid(priority_result.value())) {
return absl::nullopt;
}
return Priority(priority_result.value());
}
StatusOr<SequencingInformation> SequencingInformationValueToProto(
const base::Value& value) {
const std::string* const sequencing_id = value.FindStringKey("sequencingId");
const std::string* const generation_id = value.FindStringKey("generationId");
const auto priority_result = GetPriorityFromSequencingInformationValue(value);
// If any of the previous values don't exist, or are malformed, return error.
if (!sequencing_id || generation_id->empty() || !generation_id ||
generation_id->empty() || !priority_result.has_value() ||
!Priority_IsValid(priority_result.value())) {
return Status(error::INVALID_ARGUMENT,
base::StrCat({"Provided value lacks some fields required by "
"SequencingInformation proto: ",
value.DebugString()}));
}
int64_t seq_id;
int64_t gen_id;
if (!base::StringToInt64(*sequencing_id, &seq_id) ||
!base::StringToInt64(*generation_id, &gen_id) || gen_id == 0) {
// For backwards compatibility accept unsigned values if signed are not
// parsed.
// TODO(b/177677467): Remove this duplication once server is fully
// transitioned.
uint64_t unsigned_seq_id;
uint64_t unsigned_gen_id;
if (!base::StringToUint64(*sequencing_id, &unsigned_seq_id) ||
!base::StringToUint64(*generation_id, &unsigned_gen_id) ||
unsigned_gen_id == 0) {
return Status(error::INVALID_ARGUMENT,
base::StrCat({"Provided value did not conform to a valid "
"SequencingInformation proto: ",
value.DebugString()}));
}
seq_id = static_cast<int64_t>(unsigned_seq_id);
gen_id = static_cast<int64_t>(unsigned_gen_id);
}
SequencingInformation proto;
proto.set_sequencing_id(seq_id);
proto.set_generation_id(gen_id);
proto.set_priority(Priority(priority_result.value()));
return proto;
}
} // namespace
FakeUploadClient::FakeUploadClient(
policy::CloudPolicyClient* cloud_policy_client,
ReportSuccessfulUploadCallback report_upload_success_cb,
EncryptionKeyAttachedCallback encryption_key_attached_cb)
: cloud_policy_client_(cloud_policy_client),
report_upload_success_cb_(report_upload_success_cb),
encryption_key_attached_cb_(encryption_key_attached_cb) {}
FakeUploadClient::~FakeUploadClient() = default;
void FakeUploadClient::Create(
policy::CloudPolicyClient* cloud_policy_client,
ReportSuccessfulUploadCallback report_upload_success_cb,
EncryptionKeyAttachedCallback encryption_key_attached_cb,
CreatedCallback created_cb) {
std::move(created_cb)
.Run(base::WrapUnique<UploadClient>(new FakeUploadClient(
cloud_policy_client, std::move(report_upload_success_cb),
std::move(encryption_key_attached_cb))));
}
Status FakeUploadClient::EnqueueUpload(
bool need_encryption_keys,
std::unique_ptr<std::vector<EncryptedRecord>> records) {
UploadEncryptedReportingRequestBuilder builder;
for (auto record : *records) {
builder.AddRecord(record);
}
auto request_result = builder.Build();
if (!request_result.has_value()) {
// While it might seem that this should return a bad status, the actual
// UploadClient would return OK here as the records are processed at a later
// time, and any failures are expected to be handled elsewhere.
return Status::StatusOK();
}
auto response_cb = base::BindOnce(&FakeUploadClient::OnUploadComplete,
base::Unretained(this));
cloud_policy_client_->UploadEncryptedReport(
std::move(request_result.value()),
base::Value(base::Value::Type::DICTIONARY), std::move(response_cb));
return Status::StatusOK();
}
void FakeUploadClient::OnUploadComplete(absl::optional<base::Value> response) {
if (!response.has_value()) {
return;
}
const base::Value* last_success =
response->FindDictKey("lastSucceedUploadedRecord");
if (last_success != nullptr) {
const auto force_confirm_flag = last_success->FindBoolKey("forceConfirm");
bool force_confirm =
force_confirm_flag.has_value() && force_confirm_flag.value();
auto seq_info_result = SequencingInformationValueToProto(*last_success);
if (seq_info_result.ok()) {
report_upload_success_cb_.Run(seq_info_result.ValueOrDie(),
force_confirm);
}
}
const base::Value* signed_encryption_key_record =
response->FindDictKey("encryptionSettings");
if (signed_encryption_key_record != nullptr) {
const std::string* public_key_str =
signed_encryption_key_record->FindStringKey("publicKey");
const auto public_key_id_result =
signed_encryption_key_record->FindIntKey("publicKeyId");
const std::string* public_key_signature_str =
signed_encryption_key_record->FindStringKey("publicKeySignature");
std::string public_key;
std::string public_key_signature;
if (public_key_str != nullptr &&
base::Base64Decode(*public_key_str, &public_key) &&
public_key_signature_str != nullptr &&
base::Base64Decode(*public_key_signature_str, &public_key_signature) &&
public_key_id_result.has_value()) {
SignedEncryptionInfo signed_encryption_key;
signed_encryption_key.set_public_asymmetric_key(public_key);
signed_encryption_key.set_public_key_id(public_key_id_result.value());
signed_encryption_key.set_signature(public_key_signature);
encryption_key_attached_cb_.Run(signed_encryption_key);
}
}
}
} // namespace reporting