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