1 #pragma once
2
3 #include "app.hpp"
4 #include "async_resp.hpp"
5 #include "dbus_utility.hpp"
6 #include "error_messages.hpp"
7 #include "utils/collection.hpp"
8 #include "utils/hex_utils.hpp"
9 #include "utils/json_utils.hpp"
10
11 #include <boost/system/error_code.hpp>
12 #include <boost/url/format.hpp>
13 #include <nlohmann/json.hpp>
14
15 #include <array>
16 #include <string_view>
17 #include <vector>
18
19 namespace crow
20 {
21 namespace google_api
22 {
23
24 inline void
handleGoogleV1Get(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)25 handleGoogleV1Get(const crow::Request& /*req*/,
26 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
27 {
28 asyncResp->res.jsonValue["@odata.type"] =
29 "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot";
30 asyncResp->res.jsonValue["@odata.id"] = "/google/v1";
31 asyncResp->res.jsonValue["Id"] = "Google Rest RootService";
32 asyncResp->res.jsonValue["Name"] = "Google Service Root";
33 asyncResp->res.jsonValue["Version"] = "1.0.0";
34 asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] =
35 "/google/v1/RootOfTrustCollection";
36 }
37
handleRootOfTrustCollectionGet(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)38 inline void handleRootOfTrustCollectionGet(
39 const crow::Request& /*req*/,
40 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
41 {
42 asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection";
43 asyncResp->res.jsonValue["@odata.type"] =
44 "#RootOfTrustCollection.RootOfTrustCollection";
45 const std::array<std::string_view, 1> interfaces{
46 "xyz.openbmc_project.Control.Hoth"};
47 redfish::collection_util::getCollectionMembers(
48 asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"),
49 interfaces, "/xyz/openbmc_project");
50 }
51
52 // Helper struct to identify a resolved D-Bus object interface
53 struct ResolvedEntity
54 {
55 std::string id;
56 std::string service;
57 std::string object;
58 std::string interface;
59 };
60
61 using ResolvedEntityHandler = std::function<void(
62 const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&,
63 const ResolvedEntity&)>;
64
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)65 inline void hothGetSubtreeCallback(
66 const std::string& command,
67 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
68 const std::string& rotId, const ResolvedEntityHandler& entityHandler,
69 const boost::system::error_code& ec,
70 const dbus::utility::MapperGetSubTreeResponse& subtree)
71 {
72 if (ec)
73 {
74 redfish::messages::internalError(asyncResp->res);
75 return;
76 }
77 for (const auto& [path, services] : subtree)
78 {
79 sdbusplus::message::object_path objPath(path);
80 if (objPath.filename() != rotId || services.empty())
81 {
82 continue;
83 }
84
85 ResolvedEntity resolvedEntity = {
86 .id = rotId,
87 .service = services[0].first,
88 .object = path,
89 .interface = "xyz.openbmc_project.Control.Hoth"};
90 entityHandler(command, asyncResp, resolvedEntity);
91 return;
92 }
93
94 // Couldn't find an object with that name. return an error
95 redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId);
96 }
97
resolveRoT(const std::string & command,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & rotId,ResolvedEntityHandler && entityHandler)98 inline void resolveRoT(const std::string& command,
99 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
100 const std::string& rotId,
101 ResolvedEntityHandler&& entityHandler)
102 {
103 constexpr std::array<std::string_view, 1> hothIfaces = {
104 "xyz.openbmc_project.Control.Hoth"};
105 dbus::utility::getSubTree(
106 "/xyz/openbmc_project", 0, hothIfaces,
107 [command, asyncResp, rotId, entityHandler{std::move(entityHandler)}](
108 const boost::system::error_code& ec,
109 const dbus::utility::MapperGetSubTreeResponse& subtree) {
110 hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec,
111 subtree);
112 });
113 }
114
populateRootOfTrustEntity(const std::string &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const ResolvedEntity & resolvedEntity)115 inline void populateRootOfTrustEntity(
116 const std::string& /*unused*/,
117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
118 const ResolvedEntity& resolvedEntity)
119 {
120 asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust";
121 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
122 "/google/v1/RootOfTrustCollection/{}", resolvedEntity.id);
123
124 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
125 asyncResp->res.jsonValue["Id"] = resolvedEntity.id;
126 // Need to fix this later to a stabler property.
127 asyncResp->res.jsonValue["Name"] = resolvedEntity.id;
128 asyncResp->res.jsonValue["Description"] = "Google Root Of Trust";
129 asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] =
130 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id +
131 "/Actions/RootOfTrust.SendCommand";
132
133 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
134 resolvedEntity.id;
135 asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] =
136 "Embedded";
137 }
138
139 inline void
handleRootOfTrustGet(const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param)140 handleRootOfTrustGet(const crow::Request& /*req*/,
141 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
142 const std::string& param)
143 {
144 std::string emptyCommand;
145 resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity);
146 }
147
148 inline void
invocationCallback(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::vector<uint8_t> & responseBytes)149 invocationCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
150 const boost::system::error_code& ec,
151 const std::vector<uint8_t>& responseBytes)
152 {
153 if (ec)
154 {
155 BMCWEB_LOG_ERROR("RootOfTrust.Actions.SendCommand failed: {}",
156 ec.message());
157 redfish::messages::internalError(asyncResp->res);
158 return;
159 }
160
161 asyncResp->res.jsonValue["CommandResponse"] =
162 bytesToHexString(responseBytes);
163 }
164
165 inline void
invokeRoTCommand(const std::string & command,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const ResolvedEntity & resolvedEntity)166 invokeRoTCommand(const std::string& command,
167 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
168 const ResolvedEntity& resolvedEntity)
169 {
170 std::vector<uint8_t> bytes = hexStringToBytes(command);
171 if (bytes.empty())
172 {
173 BMCWEB_LOG_DEBUG("Invalid command: {}", command);
174 redfish::messages::actionParameterValueTypeError(command, "Command",
175 "SendCommand");
176 return;
177 }
178
179 crow::connections::systemBus->async_method_call(
180 [asyncResp{asyncResp}](const boost::system::error_code& ec,
181 const std::vector<uint8_t>& responseBytes) {
182 invocationCallback(asyncResp, ec, responseBytes);
183 },
184 resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface,
185 "SendHostCommand", bytes);
186 }
187
handleRoTSendCommandPost(const crow::Request & request,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & rotId)188 inline void handleRoTSendCommandPost(
189 const crow::Request& request,
190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
191 const std::string& rotId)
192 {
193 std::string command;
194 if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command",
195 command))
196 {
197 BMCWEB_LOG_DEBUG("Missing property Command.");
198 redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand",
199 "Command");
200 return;
201 }
202
203 resolveRoT(command, asyncResp, rotId, invokeRoTCommand);
204 }
205
requestRoutes(App & app)206 inline void requestRoutes(App& app)
207 {
208 BMCWEB_ROUTE(app, "/google/v1/")
209 .methods(boost::beast::http::verb::get)(handleGoogleV1Get);
210
211 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection")
212 .privileges({{"ConfigureManager"}})
213 .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet);
214
215 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>")
216 .privileges({{"ConfigureManager"}})
217 .methods(boost::beast::http::verb::get)(handleRootOfTrustGet);
218
219 BMCWEB_ROUTE(
220 app,
221 "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand")
222 .privileges({{"ConfigureManager"}})
223 .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost);
224 }
225
226 } // namespace google_api
227 } // namespace crow
228