1 #pragma once
2 
3 #include "app.hpp"
4 #include "error_messages.hpp"
5 #include "http_request.hpp"
6 #include "http_response.hpp"
7 #include "query.hpp"
8 #include "registries/privilege_registry.hpp"
9 #include "schemas.hpp"
10 #include "utility.hpp"
11 
12 #include <boost/url/format.hpp>
13 
14 #include <string>
15 
16 namespace redfish
17 {
18 
19 inline void redfishGet(App& app, const crow::Request& req,
20                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
21 {
22     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
23     {
24         return;
25     }
26     asyncResp->res.jsonValue["v1"] = "/redfish/v1/";
27 }
28 
29 inline void redfish404(App& app, const crow::Request& req,
30                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
31                        const std::string& path)
32 {
33     asyncResp->res.addHeader(boost::beast::http::field::allow, "");
34 
35     // If we fall to this route, we didn't have a more specific route, so return
36     // 404
37     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
38     {
39         return;
40     }
41 
42     BMCWEB_LOG_WARNING << "404 on path " << path;
43 
44     std::string name = req.url().segments().back();
45     // Note, if we hit the wildcard route, we don't know the "type" the user was
46     // actually requesting, but giving them a return with an empty string is
47     // still better than nothing.
48     messages::resourceNotFound(asyncResp->res, "", name);
49 }
50 
51 inline void redfish405(App& app, const crow::Request& req,
52                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
53                        const std::string& path)
54 {
55     // If we fall to this route, we didn't have a more specific route, so return
56     // 405
57     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
58     {
59         return;
60     }
61 
62     BMCWEB_LOG_WARNING << "405 on path " << path;
63     asyncResp->res.result(boost::beast::http::status::method_not_allowed);
64     if (req.method() == boost::beast::http::verb::delete_)
65     {
66         messages::resourceCannotBeDeleted(asyncResp->res);
67     }
68     else
69     {
70         messages::operationNotAllowed(asyncResp->res);
71     }
72 }
73 
74 inline void
75     jsonSchemaIndexGet(App& app, const crow::Request& req,
76                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
77 {
78     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
79     {
80         return;
81     }
82     nlohmann::json& json = asyncResp->res.jsonValue;
83     json["@odata.id"] = "/redfish/v1/JsonSchemas";
84     json["@odata.type"] = "#JsonSchemaFileCollection.JsonSchemaFileCollection";
85     json["Name"] = "JsonSchemaFile Collection";
86     json["Description"] = "Collection of JsonSchemaFiles";
87     nlohmann::json::array_t members;
88     for (std::string_view schema : schemas)
89     {
90         nlohmann::json::object_t member;
91         member["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}",
92                                                   schema);
93         members.emplace_back(std::move(member));
94     }
95     json["Members"] = std::move(members);
96     json["Members@odata.count"] = schemas.size();
97 }
98 
99 inline void jsonSchemaGet(App& app, const crow::Request& req,
100                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
101                           const std::string& schema)
102 {
103     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
104     {
105         return;
106     }
107 
108     if (std::find(schemas.begin(), schemas.end(), schema) == schemas.end())
109     {
110         messages::resourceNotFound(asyncResp->res, "JsonSchemaFile", schema);
111         return;
112     }
113 
114     nlohmann::json& json = asyncResp->res.jsonValue;
115     json["@odata.id"] = boost::urls::format("/redfish/v1/JsonSchemas/{}",
116                                             schema);
117     json["@odata.type"] = "#JsonSchemaFile.v1_0_2.JsonSchemaFile";
118     json["Name"] = schema + " Schema File";
119     json["Description"] = schema + " Schema File Location";
120     json["Id"] = schema;
121     std::string schemaName = "#";
122     schemaName += schema;
123     schemaName += ".";
124     schemaName += schema;
125     json["Schema"] = std::move(schemaName);
126     constexpr std::array<std::string_view, 1> languages{"en"};
127     json["Languages"] = languages;
128     json["Languages@odata.count"] = languages.size();
129 
130     nlohmann::json::array_t locationArray;
131     nlohmann::json::object_t locationEntry;
132     locationEntry["Language"] = "en";
133     locationEntry["PublicationUri"] = "http://redfish.dmtf.org/schemas/v1/" +
134                                       schema + ".json";
135     locationEntry["Uri"] = boost::urls::format(
136         "/redfish/v1/JsonSchemas/{}/{}", schema, std::string(schema) + ".json");
137 
138     locationArray.emplace_back(locationEntry);
139 
140     json["Location"] = std::move(locationArray);
141     json["Location@odata.count"] = 1;
142 }
143 
144 inline void requestRoutesRedfish(App& app)
145 {
146     BMCWEB_ROUTE(app, "/redfish/")
147         .methods(boost::beast::http::verb::get)(
148             std::bind_front(redfishGet, std::ref(app)));
149 
150     BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/<str>/")
151         .privileges(redfish::privileges::getJsonSchemaFileCollection)
152         .methods(boost::beast::http::verb::get)(
153             std::bind_front(jsonSchemaGet, std::ref(app)));
154 
155     BMCWEB_ROUTE(app, "/redfish/v1/JsonSchemas/")
156         .privileges(redfish::privileges::getJsonSchemaFile)
157         .methods(boost::beast::http::verb::get)(
158             std::bind_front(jsonSchemaIndexGet, std::ref(app)));
159 
160     // Note, this route must always be registered last
161     BMCWEB_ROUTE(app, "/redfish/<path>")
162         .notFound()
163         .privileges(redfish::privileges::privilegeSetLogin)(
164             std::bind_front(redfish404, std::ref(app)));
165 
166     BMCWEB_ROUTE(app, "/redfish/<path>")
167         .methodNotAllowed()
168         .privileges(redfish::privileges::privilegeSetLogin)(
169             std::bind_front(redfish405, std::ref(app)));
170 }
171 
172 } // namespace redfish
173