blob: 05b00e493038fe2e1bff5dc4000f24322164cb68 [file] [log] [blame]
// Copyright 2024 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/manta/scanner_provider.h"
#include <memory>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/endpoint_fetcher/endpoint_fetcher.h"
#include "components/manta/base_provider.h"
#include "components/manta/features.h"
#include "components/manta/manta_service_callbacks.h"
#include "components/manta/manta_status.h"
#include "components/manta/proto/common.pb.h"
#include "components/manta/proto/manta.pb.h"
#include "components/manta/proto/scanner.pb.h"
#include "components/signin/public/identity_manager/identity_manager.h"
namespace manta {
namespace {
constexpr char kOauthConsumerName[] = "manta_scanner";
constexpr base::TimeDelta kTimeout = base::Seconds(30);
constexpr auto kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("chromeos_scanner_provider", R"(
semantics {
sender: "ChromeOS Scanner"
description:
"Requests extracted details for a selected region of their screen "
"from the Scanner service."
trigger:
"User selecting a region via the Scanner UI."
internal {
contacts {
email: "[email protected]"
}
}
user_data {
type: USER_CONTENT
}
data:
"A selected region of their screen."
destination: GOOGLE_OWNED_SERVICE
last_reviewed: "2025-02-25"
}
policy {
cookies_allowed: NO
setting:
"No setting. Users must take explicit action to trigger the feature."
policy_exception_justification:
"Not implemented, not considered useful. This request is part of a "
"flow which is user-initiated."
}
)");
bool IsValidScannerInput(const proto::ScannerInput& scanner_input) {
return scanner_input.image().size() > 0;
}
std::unique_ptr<proto::ScannerOutput> GetScannerOutput(
const proto::Response& manta_response) {
// There should only be one output data.
if (manta_response.output_data_size() != 1) {
return nullptr;
}
const proto::OutputData& output_data = manta_response.output_data(0);
// There should be a custom proto.
if (!output_data.has_custom()) {
return nullptr;
}
// The custom proto should be a ScannerOutput.
if (output_data.custom().type_url() != kScannerOutputTypeUrl) {
return nullptr;
}
const proto::Proto3Any& custom_data = output_data.custom();
auto scanner_output = std::make_unique<proto::ScannerOutput>();
scanner_output->ParseFromString(custom_data.value());
return scanner_output;
}
void OnServerResponseOrErrorReceived(
ScannerProvider::ScannerProtoResponseCallback callback,
std::unique_ptr<proto::Response> manta_response,
MantaStatus manta_status) {
// Check for Manta error status.
if (manta_status.status_code != MantaStatusCode::kOk) {
std::move(callback).Run(nullptr, std::move(manta_status));
return;
}
// Check for a valid Manta response.
if (manta_response == nullptr) {
std::move(callback).Run(nullptr, {MantaStatusCode::kMalformedResponse});
return;
}
// Check that the Manta response has output data.
if (manta_response->output_data_size() == 0) {
std::string message;
// If there are no outputs, the response might have been filtered.
if (manta_response->filtered_data_size() > 0 &&
manta_response->filtered_data(0).is_output_data()) {
message = base::StrCat({"filtered output for: ",
proto::FilteredReason_Name(
manta_response->filtered_data(0).reason())});
}
std::move(callback).Run(nullptr,
{MantaStatusCode::kBlockedOutputs, message});
return;
}
// Try to extract a ScannerOutput object from the Manta response.
std::unique_ptr<proto::ScannerOutput> scanner_output =
GetScannerOutput(*manta_response);
if (scanner_output == nullptr) {
std::move(callback).Run(nullptr, {MantaStatusCode::kMalformedResponse});
return;
}
// All checks passed, run callback with valid ScannerOutput.
std::move(callback).Run(std::move(scanner_output), std::move(manta_status));
}
} // namespace
ScannerProvider::ScannerProvider(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
signin::IdentityManager* identity_manager,
const ProviderParams& provider_params)
: BaseProvider(url_loader_factory, identity_manager, provider_params) {}
ScannerProvider::~ScannerProvider() = default;
void ScannerProvider::Call(
const manta::proto::ScannerInput& scanner_input,
ScannerProvider::ScannerProtoResponseCallback done_callback) {
// Check for valid ScannerInput.
if (!IsValidScannerInput(scanner_input)) {
std::move(done_callback).Run(nullptr, {MantaStatusCode::kInvalidInput});
return;
}
// Populate Manta request with the specified ScannerInput as the custom input
// data.
proto::Request request;
request.set_feature_name(proto::FeatureName::CHROMEOS_SCANNER);
proto::InputData* input_data = request.add_input_data();
input_data->set_tag("scanner_input");
proto::Proto3Any& custom = *input_data->mutable_custom();
custom.set_type_url(kScannerInputTypeUrl);
custom.set_value(scanner_input.SerializeAsString());
RequestInternal(
GURL{GetProviderEndpoint(features::IsScannerUseProdServerEnabled())},
kOauthConsumerName, /*annotation_tag=*/kTrafficAnnotation, request,
MantaMetricType::kScanner,
base::BindOnce(&OnServerResponseOrErrorReceived,
std::move(done_callback)),
kTimeout);
}
} // namespace manta