177dd8813SKowalski, Kamil /* 277dd8813SKowalski, Kamil // Copyright (c) 2018 Intel Corporation 377dd8813SKowalski, Kamil // 477dd8813SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License"); 577dd8813SKowalski, Kamil // you may not use this file except in compliance with the License. 677dd8813SKowalski, Kamil // You may obtain a copy of the License at 777dd8813SKowalski, Kamil // 877dd8813SKowalski, Kamil // http://www.apache.org/licenses/LICENSE-2.0 977dd8813SKowalski, Kamil // 1077dd8813SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software 1177dd8813SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS, 1277dd8813SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377dd8813SKowalski, Kamil // See the License for the specific language governing permissions and 1477dd8813SKowalski, Kamil // limitations under the License. 1577dd8813SKowalski, Kamil */ 1677dd8813SKowalski, Kamil #pragma once 179712f8acSEd Tanous 1877dd8813SKowalski, Kamil #include <crow/http_request.h> 1977dd8813SKowalski, Kamil #include <crow/http_response.h> 2077dd8813SKowalski, Kamil 219712f8acSEd Tanous #include <bitset> 229712f8acSEd Tanous #include <error_messages.hpp> 231abe55efSEd Tanous #include <nlohmann/json.hpp> 241abe55efSEd Tanous namespace redfish 251abe55efSEd Tanous { 261abe55efSEd Tanous 271abe55efSEd Tanous namespace json_util 281abe55efSEd Tanous { 2977dd8813SKowalski, Kamil 3077dd8813SKowalski, Kamil /** 3177dd8813SKowalski, Kamil * @brief Processes request to extract JSON from its body. If it fails, adds 3277dd8813SKowalski, Kamil * MalformedJSON message to response and ends it. 3377dd8813SKowalski, Kamil * 3477dd8813SKowalski, Kamil * @param[io] res Response object 3577dd8813SKowalski, Kamil * @param[in] req Request object 3677dd8813SKowalski, Kamil * @param[out] reqJson JSON object extracted from request's body 3777dd8813SKowalski, Kamil * 3877dd8813SKowalski, Kamil * @return true if JSON is valid, false when JSON is invalid and response has 3977dd8813SKowalski, Kamil * been filled with message and ended. 4077dd8813SKowalski, Kamil */ 4155c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 4277dd8813SKowalski, Kamil nlohmann::json& reqJson); 439712f8acSEd Tanous namespace details 449712f8acSEd Tanous { 45*771cfa0fSJason M. Bills 46*771cfa0fSJason M. Bills template <typename Type> struct is_optional : std::false_type 479712f8acSEd Tanous { 489712f8acSEd Tanous }; 499712f8acSEd Tanous 50*771cfa0fSJason M. Bills template <typename Type> 51*771cfa0fSJason M. Bills struct is_optional<boost::optional<Type>> : std::true_type 529712f8acSEd Tanous { 539712f8acSEd Tanous }; 549712f8acSEd Tanous 55*771cfa0fSJason M. Bills template <typename Type> 56*771cfa0fSJason M. Bills constexpr bool is_optional_v = is_optional<Type>::value; 57*771cfa0fSJason M. Bills 58*771cfa0fSJason M. Bills template <typename Type> 59*771cfa0fSJason M. Bills void unpackValue(nlohmann::json& jsonValue, const std::string& key, 60*771cfa0fSJason M. Bills crow::Response& res, Type& value) 61*771cfa0fSJason M. Bills { 62*771cfa0fSJason M. Bills if constexpr (std::is_arithmetic_v<Type>) 63*771cfa0fSJason M. Bills { 64*771cfa0fSJason M. Bills using NumType = 65*771cfa0fSJason M. Bills std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 66*771cfa0fSJason M. Bills 67*771cfa0fSJason M. Bills NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 68*771cfa0fSJason M. Bills if (jsonPtr == nullptr) 69*771cfa0fSJason M. Bills { 70*771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 71*771cfa0fSJason M. Bills << "Value for key " << key 72*771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 73*771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 74*771cfa0fSJason M. Bills return; 75*771cfa0fSJason M. Bills } 76*771cfa0fSJason M. Bills if (*jsonPtr > std::numeric_limits<Type>::max()) 77*771cfa0fSJason M. Bills { 78*771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 79*771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 80*771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 81*771cfa0fSJason M. Bills return; 82*771cfa0fSJason M. Bills } 83*771cfa0fSJason M. Bills if (*jsonPtr < std::numeric_limits<Type>::min()) 84*771cfa0fSJason M. Bills { 85*771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 86*771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 87*771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 88*771cfa0fSJason M. Bills return; 89*771cfa0fSJason M. Bills } 90*771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 91*771cfa0fSJason M. Bills } 92*771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 93*771cfa0fSJason M. Bills { 94*771cfa0fSJason M. Bills value.emplace(); 95*771cfa0fSJason M. Bills unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 96*771cfa0fSJason M. Bills } 97*771cfa0fSJason M. Bills else 98*771cfa0fSJason M. Bills { 99*771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 100*771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 101*771cfa0fSJason M. Bills if (jsonPtr == nullptr) 102*771cfa0fSJason M. Bills { 103*771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 104*771cfa0fSJason M. Bills << "Value for key " << key 105*771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 106*771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 107*771cfa0fSJason M. Bills return; 108*771cfa0fSJason M. Bills } 109*771cfa0fSJason M. Bills value = std::move(*jsonPtr); 110*771cfa0fSJason M. Bills } 111*771cfa0fSJason M. Bills } 112*771cfa0fSJason M. Bills 1139712f8acSEd Tanous template <size_t Count, size_t Index> 1149712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1159712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 1169712f8acSEd Tanous { 1179712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 118a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 1199712f8acSEd Tanous } 1209712f8acSEd Tanous 1219712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 1229712f8acSEd Tanous typename... UnpackTypes> 1239712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1249712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 1259712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 1269712f8acSEd Tanous UnpackTypes&... in) 1279712f8acSEd Tanous { 1289712f8acSEd Tanous if (key != keyToCheck) 1299712f8acSEd Tanous { 1309712f8acSEd Tanous readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 1319712f8acSEd Tanous return; 1329712f8acSEd Tanous } 1339712f8acSEd Tanous 1349712f8acSEd Tanous handled.set(Index); 1359712f8acSEd Tanous 136*771cfa0fSJason M. Bills unpackValue<ValueType>(jsonValue, key, res, valueToFill); 1379712f8acSEd Tanous } 1389712f8acSEd Tanous 1399712f8acSEd Tanous template <size_t Index = 0, size_t Count> 1409712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res) 1419712f8acSEd Tanous { 1429712f8acSEd Tanous } 1439712f8acSEd Tanous 1449712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 1459712f8acSEd Tanous typename... UnpackTypes> 1469712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res, 1479712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 1489712f8acSEd Tanous { 149*771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 1509712f8acSEd Tanous { 151a08b46ccSJason M. Bills messages::propertyMissing(res, key); 1529712f8acSEd Tanous } 1539712f8acSEd Tanous details::handleMissing<Index + 1, Count>(handled, res, in...); 1549712f8acSEd Tanous } 1559712f8acSEd Tanous } // namespace details 1569712f8acSEd Tanous 1579712f8acSEd Tanous template <typename... UnpackTypes> 1589712f8acSEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 1599712f8acSEd Tanous UnpackTypes&... in) 1609712f8acSEd Tanous { 1619712f8acSEd Tanous nlohmann::json jsonRequest; 1629712f8acSEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 1639712f8acSEd Tanous { 1649712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 1659712f8acSEd Tanous return false; 1669712f8acSEd Tanous } 1679712f8acSEd Tanous if (!jsonRequest.is_object()) 1689712f8acSEd Tanous { 1699712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 170f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 1719712f8acSEd Tanous return false; 1729712f8acSEd Tanous } 1739712f8acSEd Tanous 1749712f8acSEd Tanous if (jsonRequest.empty()) 1759712f8acSEd Tanous { 1769712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 177f12894f8SJason M. Bills messages::emptyJSON(res); 1789712f8acSEd Tanous return false; 1799712f8acSEd Tanous } 1809712f8acSEd Tanous 1819712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 1829712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 1839712f8acSEd Tanous { 1849712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 1859712f8acSEd Tanous item.key(), item.value(), res, handled, key, in...); 1869712f8acSEd Tanous } 1879712f8acSEd Tanous 1889712f8acSEd Tanous details::handleMissing(handled, res, key, in...); 1899712f8acSEd Tanous 1909712f8acSEd Tanous return res.result() == boost::beast::http::status::ok; 1919712f8acSEd Tanous } 19277dd8813SKowalski, Kamil 19377dd8813SKowalski, Kamil } // namespace json_util 19477dd8813SKowalski, Kamil } // namespace redfish 195