blob: 9b7a909c01979b58c43e4f09222e8b8e5f950921 [file] [log] [blame]
#pragma once
#include "app.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "utils/collection.hpp"
#include "utils/hex_utils.hpp"
#include "utils/json_utils.hpp"
#include <boost/system/error_code.hpp>
#include <boost/url/format.hpp>
#include <nlohmann/json.hpp>
#include <array>
#include <string_view>
#include <vector>
namespace crow
{
namespace google_api
{
inline void
handleGoogleV1Get(const crow::Request& /*req*/,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
asyncResp->res.jsonValue["@odata.type"] =
"#GoogleServiceRoot.v1_0_0.GoogleServiceRoot";
asyncResp->res.jsonValue["@odata.id"] = "/google/v1";
asyncResp->res.jsonValue["Id"] = "Google Rest RootService";
asyncResp->res.jsonValue["Name"] = "Google Service Root";
asyncResp->res.jsonValue["Version"] = "1.0.0";
asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] =
"/google/v1/RootOfTrustCollection";
}
inline void handleRootOfTrustCollectionGet(
const crow::Request& /*req*/,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection";
asyncResp->res.jsonValue["@odata.type"] =
"#RootOfTrustCollection.RootOfTrustCollection";
const std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Control.Hoth"};
redfish::collection_util::getCollectionMembers(
asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"),
interfaces, "/xyz/openbmc_project");
}
// Helper struct to identify a resolved D-Bus object interface
struct ResolvedEntity
{
std::string id;
std::string service;
std::string object;
std::string interface;
};
using ResolvedEntityHandler = std::function<void(
const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&,
const ResolvedEntity&)>;
inline void hothGetSubtreeCallback(
const std::string& command,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& rotId, const ResolvedEntityHandler& entityHandler,
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree)
{
if (ec)
{
redfish::messages::internalError(asyncResp->res);
return;
}
for (const auto& [path, services] : subtree)
{
sdbusplus::message::object_path objPath(path);
if (objPath.filename() != rotId || services.empty())
{
continue;
}
ResolvedEntity resolvedEntity = {
.id = rotId,
.service = services[0].first,
.object = path,
.interface = "xyz.openbmc_project.Control.Hoth"};
entityHandler(command, asyncResp, resolvedEntity);
return;
}
// Couldn't find an object with that name. return an error
redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId);
}
inline void resolveRoT(const std::string& command,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& rotId,
ResolvedEntityHandler&& entityHandler)
{
constexpr std::array<std::string_view, 1> hothIfaces = {
"xyz.openbmc_project.Control.Hoth"};
dbus::utility::getSubTree(
"/xyz/openbmc_project", 0, hothIfaces,
[command, asyncResp, rotId, entityHandler{std::move(entityHandler)}](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec,
subtree);
});
}
inline void populateRootOfTrustEntity(
const std::string& /*unused*/,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const ResolvedEntity& resolvedEntity)
{
asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust";
asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
"/google/v1/RootOfTrustCollection/{}", resolvedEntity.id);
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
asyncResp->res.jsonValue["Id"] = resolvedEntity.id;
// Need to fix this later to a stabler property.
asyncResp->res.jsonValue["Name"] = resolvedEntity.id;
asyncResp->res.jsonValue["Description"] = "Google Root Of Trust";
asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] =
"/google/v1/RootOfTrustCollection/" + resolvedEntity.id +
"/Actions/RootOfTrust.SendCommand";
asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
resolvedEntity.id;
asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] =
"Embedded";
}
inline void
handleRootOfTrustGet(const crow::Request& /*req*/,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& param)
{
std::string emptyCommand;
resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity);
}
inline void
invocationCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const boost::system::error_code& ec,
const std::vector<uint8_t>& responseBytes)
{
if (ec)
{
BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}",
ec.message());
redfish::messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["CommandResponse"] =
bytesToHexString(responseBytes);
}
inline void
invokeRoTCommand(const std::string& command,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const ResolvedEntity& resolvedEntity)
{
std::vector<uint8_t> bytes = hexStringToBytes(command);
if (bytes.empty())
{
BMCWEB_LOG_DEBUG("Invalid command: {}", command);
redfish::messages::actionParameterValueTypeError(command, "Command",
"SendCommand");
return;
}
crow::connections::systemBus->async_method_call(
[asyncResp{asyncResp}](const boost::system::error_code& ec,
const std::vector<uint8_t>& responseBytes) {
invocationCallback(asyncResp, ec, responseBytes);
},
resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface,
"SendHostCommand", bytes);
}
inline void handleRoTSendCommandPost(
const crow::Request& request,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& rotId)
{
std::string command;
if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command",
command))
{
BMCWEB_LOG_DEBUG("Missing property Command.");
redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand",
"Command");
return;
}
resolveRoT(command, asyncResp, rotId, invokeRoTCommand);
}
inline void requestRoutes(App& app)
{
BMCWEB_ROUTE(app, "/google/v1/")
.methods(boost::beast::http::verb::get)(handleGoogleV1Get);
BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection")
.privileges({{"ConfigureManager"}})
.methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet);
BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>")
.privileges({{"ConfigureManager"}})
.methods(boost::beast::http::verb::get)(handleRootOfTrustGet);
BMCWEB_ROUTE(
app,
"/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand")
.privileges({{"ConfigureManager"}})
.methods(boost::beast::http::verb::post)(handleRoTSendCommandPost);
}
} // namespace google_api
} // namespace crow