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 <nlohmann/json.hpp>
13 
14 #include <array>
15 #include <string_view>
16 #include <vector>
17 
18 namespace crow
19 {
20 namespace google_api
21 {
22 
23 inline void
24     handleGoogleV1Get(const crow::Request& /*req*/,
25                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
26 {
27     asyncResp->res.jsonValue["@odata.type"] =
28         "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot";
29     asyncResp->res.jsonValue["@odata.id"] = "/google/v1";
30     asyncResp->res.jsonValue["Id"] = "Google Rest RootService";
31     asyncResp->res.jsonValue["Name"] = "Google Service Root";
32     asyncResp->res.jsonValue["Version"] = "1.0.0";
33     asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] =
34         "/google/v1/RootOfTrustCollection";
35 }
36 
37 inline void handleRootOfTrustCollectionGet(
38     const crow::Request& /*req*/,
39     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
40 {
41     asyncResp->res.jsonValue["@odata.id"] = "/google/v1/RootOfTrustCollection";
42     asyncResp->res.jsonValue["@odata.type"] =
43         "#RootOfTrustCollection.RootOfTrustCollection";
44     const std::array<std::string_view, 1> interfaces{
45         "xyz.openbmc_project.Control.Hoth"};
46     redfish::collection_util::getCollectionMembers(
47         asyncResp, boost::urls::url("/google/v1/RootOfTrustCollection"),
48         interfaces, "/xyz/openbmc_project");
49 }
50 
51 // Helper struct to identify a resolved D-Bus object interface
52 struct ResolvedEntity
53 {
54     std::string id;
55     std::string service;
56     std::string object;
57     std::string interface;
58 };
59 
60 using ResolvedEntityHandler = std::function<void(
61     const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&,
62     const ResolvedEntity&)>;
63 
64 inline void hothGetSubtreeCallback(
65     const std::string& command,
66     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
67     const std::string& rotId, const ResolvedEntityHandler& entityHandler,
68     const boost::system::error_code ec,
69     const dbus::utility::MapperGetSubTreeResponse& subtree)
70 {
71     if (ec)
72     {
73         redfish::messages::internalError(asyncResp->res);
74         return;
75     }
76     for (const auto& [path, services] : subtree)
77     {
78         sdbusplus::message::object_path objPath(path);
79         if (objPath.filename() != rotId || services.empty())
80         {
81             continue;
82         }
83 
84         ResolvedEntity resolvedEntity = {
85             .id = rotId,
86             .service = services[0].first,
87             .object = path,
88             .interface = "xyz.openbmc_project.Control.Hoth"};
89         entityHandler(command, asyncResp, resolvedEntity);
90         return;
91     }
92 
93     // Couldn't find an object with that name.  return an error
94     redfish::messages::resourceNotFound(asyncResp->res, "RootOfTrust", rotId);
95 }
96 
97 inline void resolveRoT(const std::string& command,
98                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
99                        const std::string& rotId,
100                        ResolvedEntityHandler&& entityHandler)
101 {
102     constexpr std::array<std::string_view, 1> hothIfaces = {
103         "xyz.openbmc_project.Control.Hoth"};
104     dbus::utility::getSubTree(
105         "/xyz/openbmc_project", 0, hothIfaces,
106         [command, asyncResp, rotId,
107          entityHandler{std::forward<ResolvedEntityHandler>(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 
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"] =
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
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
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
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 
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 
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