xref: /openbmc/bmcweb/redfish-core/include/query.hpp (revision 2d6cb56b6b47c3fbb0d234ade5c1208edb69ef1f)
1f4c99e70SEd Tanous #pragma once
2f4c99e70SEd Tanous 
3e796c262SNan Zhou #include "bmcweb_config.h"
4e796c262SNan Zhou 
5e796c262SNan Zhou #include "app.hpp"
6e796c262SNan Zhou #include "async_resp.hpp"
7e796c262SNan Zhou #include "error_messages.hpp"
8e796c262SNan Zhou #include "http_request.hpp"
9e796c262SNan Zhou #include "http_response.hpp"
10e796c262SNan Zhou #include "logging.hpp"
11f4c99e70SEd Tanous #include "utils/query_param.hpp"
12f4c99e70SEd Tanous 
13e796c262SNan Zhou #include <boost/beast/http/verb.hpp>
14e796c262SNan Zhou #include <boost/url/params_view.hpp>
15e796c262SNan Zhou #include <boost/url/url_view.hpp>
16e796c262SNan Zhou 
17e796c262SNan Zhou #include <functional>
18e796c262SNan Zhou #include <memory>
19e796c262SNan Zhou #include <new>
20e796c262SNan Zhou #include <optional>
21e796c262SNan Zhou #include <string>
22e796c262SNan Zhou #include <string_view>
23e796c262SNan Zhou #include <type_traits>
24e796c262SNan Zhou #include <utility>
25e796c262SNan Zhou 
26e796c262SNan Zhou // IWYU pragma: no_forward_declare crow::App
27e796c262SNan Zhou // IWYU pragma: no_include <boost/url/impl/params_view.hpp>
28e796c262SNan Zhou // IWYU pragma: no_include <boost/url/impl/url_view.hpp>
29f4c99e70SEd Tanous 
3005916cefSCarson Labrado #include <redfish_aggregator.hpp>
3105916cefSCarson Labrado 
32f4c99e70SEd Tanous namespace redfish
33f4c99e70SEd Tanous {
34*2d6cb56bSEd Tanous inline void
35*2d6cb56bSEd Tanous     afterIfMatchRequest(crow::App& app,
36*2d6cb56bSEd Tanous                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
37*2d6cb56bSEd Tanous                         crow::Request& req, const std::string& ifMatchHeader,
38*2d6cb56bSEd Tanous                         const crow::Response& resIn)
39*2d6cb56bSEd Tanous {
40*2d6cb56bSEd Tanous     std::string computedEtag = resIn.computeEtag();
41*2d6cb56bSEd Tanous     BMCWEB_LOG_DEBUG << "User provided if-match etag " << ifMatchHeader
42*2d6cb56bSEd Tanous                      << " computed etag " << computedEtag;
43*2d6cb56bSEd Tanous     if (computedEtag != ifMatchHeader)
44*2d6cb56bSEd Tanous     {
45*2d6cb56bSEd Tanous         messages::preconditionFailed(asyncResp->res);
46*2d6cb56bSEd Tanous         return;
47*2d6cb56bSEd Tanous     }
48*2d6cb56bSEd Tanous     // Restart the request without if-match
49*2d6cb56bSEd Tanous     req.req.erase(boost::beast::http::field::if_match);
50*2d6cb56bSEd Tanous     BMCWEB_LOG_DEBUG << "Restarting request";
51*2d6cb56bSEd Tanous     app.handle(req, asyncResp);
52*2d6cb56bSEd Tanous }
53*2d6cb56bSEd Tanous 
54*2d6cb56bSEd Tanous inline bool handleIfMatch(crow::App& app, const crow::Request& req,
55*2d6cb56bSEd Tanous                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
56*2d6cb56bSEd Tanous {
57*2d6cb56bSEd Tanous     if (req.session == nullptr)
58*2d6cb56bSEd Tanous     {
59*2d6cb56bSEd Tanous         // If the user isn't authenticated, don't even attempt to parse match
60*2d6cb56bSEd Tanous         // parameters
61*2d6cb56bSEd Tanous         return true;
62*2d6cb56bSEd Tanous     }
63*2d6cb56bSEd Tanous 
64*2d6cb56bSEd Tanous     std::string ifMatch{
65*2d6cb56bSEd Tanous         req.getHeaderValue(boost::beast::http::field::if_match)};
66*2d6cb56bSEd Tanous     if (ifMatch.empty())
67*2d6cb56bSEd Tanous     {
68*2d6cb56bSEd Tanous         // No If-Match header.  Nothing to do
69*2d6cb56bSEd Tanous         return true;
70*2d6cb56bSEd Tanous     }
71*2d6cb56bSEd Tanous     if (req.req.method() != boost::beast::http::verb::patch &&
72*2d6cb56bSEd Tanous         req.req.method() != boost::beast::http::verb::post &&
73*2d6cb56bSEd Tanous         req.req.method() != boost::beast::http::verb::delete_)
74*2d6cb56bSEd Tanous     {
75*2d6cb56bSEd Tanous         messages::preconditionFailed(asyncResp->res);
76*2d6cb56bSEd Tanous         return false;
77*2d6cb56bSEd Tanous     }
78*2d6cb56bSEd Tanous     boost::system::error_code ec;
79*2d6cb56bSEd Tanous 
80*2d6cb56bSEd Tanous     // Try to GET the same resource
81*2d6cb56bSEd Tanous     crow::Request newReq({boost::beast::http::verb::get, req.url, 11}, ec);
82*2d6cb56bSEd Tanous 
83*2d6cb56bSEd Tanous     if (ec)
84*2d6cb56bSEd Tanous     {
85*2d6cb56bSEd Tanous         messages::internalError(asyncResp->res);
86*2d6cb56bSEd Tanous         return false;
87*2d6cb56bSEd Tanous     }
88*2d6cb56bSEd Tanous 
89*2d6cb56bSEd Tanous     // New request has the same credentials as the old request
90*2d6cb56bSEd Tanous     newReq.session = req.session;
91*2d6cb56bSEd Tanous 
92*2d6cb56bSEd Tanous     // Construct a new response object to fill in, and check the hash of before
93*2d6cb56bSEd Tanous     // we modify the Resource.
94*2d6cb56bSEd Tanous     std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp =
95*2d6cb56bSEd Tanous         std::make_shared<bmcweb::AsyncResp>();
96*2d6cb56bSEd Tanous 
97*2d6cb56bSEd Tanous     getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front(
98*2d6cb56bSEd Tanous         afterIfMatchRequest, std::ref(app), asyncResp, req, ifMatch));
99*2d6cb56bSEd Tanous 
100*2d6cb56bSEd Tanous     app.handle(newReq, getReqAsyncResp);
101*2d6cb56bSEd Tanous     return false;
102*2d6cb56bSEd Tanous }
103f4c99e70SEd Tanous 
104a6b9125fSNan Zhou // Sets up the Redfish Route and delegates some of the query parameter
105a6b9125fSNan Zhou // processing. |queryCapabilities| stores which query parameters will be
106a6b9125fSNan Zhou // handled by redfish-core/lib codes, then default query parameter handler won't
107a6b9125fSNan Zhou // process these parameters.
108a6b9125fSNan Zhou [[nodiscard]] inline bool setUpRedfishRouteWithDelegation(
1093ba00073SCarson Labrado     crow::App& app, const crow::Request& req,
1103ba00073SCarson Labrado     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
111a6b9125fSNan Zhou     query_param::Query& delegated,
112a6b9125fSNan Zhou     const query_param::QueryCapabilities& queryCapabilities)
113f4c99e70SEd Tanous {
114142ec9aeSEd Tanous     BMCWEB_LOG_DEBUG << "setup redfish route";
115142ec9aeSEd Tanous 
116142ec9aeSEd Tanous     // Section 7.4 of the redfish spec "Redfish Services shall process the
117142ec9aeSEd Tanous     // [OData-Version header] in the following table as defined by the HTTP 1.1
118142ec9aeSEd Tanous     // specification..."
119142ec9aeSEd Tanous     // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION
120142ec9aeSEd Tanous     std::string_view odataHeader = req.getHeaderValue("OData-Version");
121142ec9aeSEd Tanous     if (!odataHeader.empty() && odataHeader != "4.0")
122142ec9aeSEd Tanous     {
1233ba00073SCarson Labrado         messages::preconditionFailed(asyncResp->res);
124142ec9aeSEd Tanous         return false;
125142ec9aeSEd Tanous     }
126142ec9aeSEd Tanous 
1273ba00073SCarson Labrado     asyncResp->res.addHeader("OData-Version", "4.0");
128c02a74f8SEd Tanous 
129f4c99e70SEd Tanous     std::optional<query_param::Query> queryOpt =
1303ba00073SCarson Labrado         query_param::parseParameters(req.urlView.params(), asyncResp->res);
131f4c99e70SEd Tanous     if (queryOpt == std::nullopt)
132f4c99e70SEd Tanous     {
133f4c99e70SEd Tanous         return false;
134f4c99e70SEd Tanous     }
135f4c99e70SEd Tanous 
136*2d6cb56bSEd Tanous     if (!handleIfMatch(app, req, asyncResp))
137*2d6cb56bSEd Tanous     {
138*2d6cb56bSEd Tanous         return false;
139*2d6cb56bSEd Tanous     }
140*2d6cb56bSEd Tanous 
14105916cefSCarson Labrado     bool needToCallHandlers = true;
14205916cefSCarson Labrado 
14305916cefSCarson Labrado #ifdef BMCWEB_ENABLE_REDFISH_AGGREGATION
14405916cefSCarson Labrado     needToCallHandlers = RedfishAggregator::getInstance().beginAggregation(
14505916cefSCarson Labrado                              req, asyncResp) == Result::LocalHandle;
14605916cefSCarson Labrado 
14705916cefSCarson Labrado     // If the request should be forwarded to a satellite BMC then we don't want
14805916cefSCarson Labrado     // to write anything to the asyncResp since it will get overwritten later.
14905916cefSCarson Labrado #endif
15005916cefSCarson Labrado 
1517cf436c9SEd Tanous     // If this isn't a get, no need to do anything with parameters
1527cf436c9SEd Tanous     if (req.method() != boost::beast::http::verb::get)
1537cf436c9SEd Tanous     {
15405916cefSCarson Labrado         return needToCallHandlers;
1557cf436c9SEd Tanous     }
1567cf436c9SEd Tanous 
157a6b9125fSNan Zhou     delegated = query_param::delegate(queryCapabilities, *queryOpt);
158f4c99e70SEd Tanous     std::function<void(crow::Response&)> handler =
1593ba00073SCarson Labrado         asyncResp->res.releaseCompleteRequestHandler();
160827c4902SNan Zhou 
1613ba00073SCarson Labrado     asyncResp->res.setCompleteRequestHandler(
162f4c99e70SEd Tanous         [&app, handler(std::move(handler)),
163827c4902SNan Zhou          query{std::move(*queryOpt)}](crow::Response& resIn) mutable {
1648a592810SEd Tanous         processAllParams(app, query, handler, resIn);
165f4c99e70SEd Tanous     });
166827c4902SNan Zhou 
16705916cefSCarson Labrado     return needToCallHandlers;
168f4c99e70SEd Tanous }
169a6b9125fSNan Zhou 
170a6b9125fSNan Zhou // Sets up the Redfish Route. All parameters are handled by the default handler.
1713ba00073SCarson Labrado [[nodiscard]] inline bool
1723ba00073SCarson Labrado     setUpRedfishRoute(crow::App& app, const crow::Request& req,
1733ba00073SCarson Labrado                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
174a6b9125fSNan Zhou {
175a6b9125fSNan Zhou     // This route |delegated| is never used
176a6b9125fSNan Zhou     query_param::Query delegated;
1773ba00073SCarson Labrado     return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated,
178a6b9125fSNan Zhou                                            query_param::QueryCapabilities{});
179a6b9125fSNan Zhou }
180f4c99e70SEd Tanous } // namespace redfish
181