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