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