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 { 45771cfa0fSJason M. Bills 46771cfa0fSJason M. Bills template <typename Type> struct is_optional : std::false_type 479712f8acSEd Tanous { 489712f8acSEd Tanous }; 499712f8acSEd Tanous 50771cfa0fSJason M. Bills template <typename Type> 51771cfa0fSJason M. Bills struct is_optional<boost::optional<Type>> : std::true_type 529712f8acSEd Tanous { 539712f8acSEd Tanous }; 549712f8acSEd Tanous 55771cfa0fSJason M. Bills template <typename Type> 56771cfa0fSJason M. Bills constexpr bool is_optional_v = is_optional<Type>::value; 57771cfa0fSJason M. Bills 58*b1556427SEd Tanous template <typename Type> struct is_vector : std::false_type 59*b1556427SEd Tanous { 60*b1556427SEd Tanous }; 61*b1556427SEd Tanous 62*b1556427SEd Tanous template <typename Type> struct is_vector<std::vector<Type>> : std::true_type 63*b1556427SEd Tanous { 64*b1556427SEd Tanous }; 65*b1556427SEd Tanous 66*b1556427SEd Tanous template <typename Type> constexpr bool is_vector_v = is_vector<Type>::value; 67*b1556427SEd Tanous 68771cfa0fSJason M. Bills template <typename Type> 69771cfa0fSJason M. Bills void unpackValue(nlohmann::json& jsonValue, const std::string& key, 70771cfa0fSJason M. Bills crow::Response& res, Type& value) 71771cfa0fSJason M. Bills { 72771cfa0fSJason M. Bills if constexpr (std::is_arithmetic_v<Type>) 73771cfa0fSJason M. Bills { 74771cfa0fSJason M. Bills using NumType = 75771cfa0fSJason M. Bills std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 76771cfa0fSJason M. Bills 77771cfa0fSJason M. Bills NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 78771cfa0fSJason M. Bills if (jsonPtr == nullptr) 79771cfa0fSJason M. Bills { 80771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 81771cfa0fSJason M. Bills << "Value for key " << key 82771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 83771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 84771cfa0fSJason M. Bills return; 85771cfa0fSJason M. Bills } 86771cfa0fSJason M. Bills if (*jsonPtr > std::numeric_limits<Type>::max()) 87771cfa0fSJason M. Bills { 88771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 89771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 90771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 91771cfa0fSJason M. Bills return; 92771cfa0fSJason M. Bills } 93771cfa0fSJason M. Bills if (*jsonPtr < std::numeric_limits<Type>::min()) 94771cfa0fSJason M. Bills { 95771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 96771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 97771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 98771cfa0fSJason M. Bills return; 99771cfa0fSJason M. Bills } 100771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 101771cfa0fSJason M. Bills } 102771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 103771cfa0fSJason M. Bills { 104771cfa0fSJason M. Bills value.emplace(); 105771cfa0fSJason M. Bills unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 106771cfa0fSJason M. Bills } 107*b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 108*b1556427SEd Tanous { 109*b1556427SEd Tanous if (!jsonValue.is_array()) 110*b1556427SEd Tanous { 111*b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 112*b1556427SEd Tanous return; 113*b1556427SEd Tanous } 114*b1556427SEd Tanous 115*b1556427SEd Tanous for (const auto& val : jsonValue.items()) 116*b1556427SEd Tanous { 117*b1556427SEd Tanous value.emplace_back(); 118*b1556427SEd Tanous unpackValue<typename Type::value_type>(val.value(), key, res, 119*b1556427SEd Tanous value.back()); 120*b1556427SEd Tanous } 121*b1556427SEd Tanous } 122771cfa0fSJason M. Bills else 123771cfa0fSJason M. Bills { 124771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 125771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 126771cfa0fSJason M. Bills if (jsonPtr == nullptr) 127771cfa0fSJason M. Bills { 128771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 129771cfa0fSJason M. Bills << "Value for key " << key 130771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 131771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 132771cfa0fSJason M. Bills return; 133771cfa0fSJason M. Bills } 134771cfa0fSJason M. Bills value = std::move(*jsonPtr); 135771cfa0fSJason M. Bills } 136771cfa0fSJason M. Bills } 137771cfa0fSJason M. Bills 1389712f8acSEd Tanous template <size_t Count, size_t Index> 1399712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1409712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 1419712f8acSEd Tanous { 1429712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 143a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 1449712f8acSEd Tanous } 1459712f8acSEd Tanous 1469712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 1479712f8acSEd Tanous typename... UnpackTypes> 1489712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1499712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 1509712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 1519712f8acSEd Tanous UnpackTypes&... in) 1529712f8acSEd Tanous { 1539712f8acSEd Tanous if (key != keyToCheck) 1549712f8acSEd Tanous { 1559712f8acSEd Tanous readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 1569712f8acSEd Tanous return; 1579712f8acSEd Tanous } 1589712f8acSEd Tanous 1599712f8acSEd Tanous handled.set(Index); 1609712f8acSEd Tanous 161771cfa0fSJason M. Bills unpackValue<ValueType>(jsonValue, key, res, valueToFill); 1629712f8acSEd Tanous } 1639712f8acSEd Tanous 1649712f8acSEd Tanous template <size_t Index = 0, size_t Count> 1659712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res) 1669712f8acSEd Tanous { 1679712f8acSEd Tanous } 1689712f8acSEd Tanous 1699712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 1709712f8acSEd Tanous typename... UnpackTypes> 1719712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res, 1729712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 1739712f8acSEd Tanous { 174771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 1759712f8acSEd Tanous { 176a08b46ccSJason M. Bills messages::propertyMissing(res, key); 1779712f8acSEd Tanous } 1789712f8acSEd Tanous details::handleMissing<Index + 1, Count>(handled, res, in...); 1799712f8acSEd Tanous } 1809712f8acSEd Tanous } // namespace details 1819712f8acSEd Tanous 1829712f8acSEd Tanous template <typename... UnpackTypes> 1839712f8acSEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 1849712f8acSEd Tanous UnpackTypes&... in) 1859712f8acSEd Tanous { 1869712f8acSEd Tanous nlohmann::json jsonRequest; 1879712f8acSEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 1889712f8acSEd Tanous { 1899712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 1909712f8acSEd Tanous return false; 1919712f8acSEd Tanous } 1929712f8acSEd Tanous if (!jsonRequest.is_object()) 1939712f8acSEd Tanous { 1949712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 195f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 1969712f8acSEd Tanous return false; 1979712f8acSEd Tanous } 1989712f8acSEd Tanous 1999712f8acSEd Tanous if (jsonRequest.empty()) 2009712f8acSEd Tanous { 2019712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 202f12894f8SJason M. Bills messages::emptyJSON(res); 2039712f8acSEd Tanous return false; 2049712f8acSEd Tanous } 2059712f8acSEd Tanous 2069712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 2079712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 2089712f8acSEd Tanous { 2099712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 2109712f8acSEd Tanous item.key(), item.value(), res, handled, key, in...); 2119712f8acSEd Tanous } 2129712f8acSEd Tanous 2139712f8acSEd Tanous details::handleMissing(handled, res, key, in...); 2149712f8acSEd Tanous 2159712f8acSEd Tanous return res.result() == boost::beast::http::status::ok; 2169712f8acSEd Tanous } 21777dd8813SKowalski, Kamil 21877dd8813SKowalski, Kamil } // namespace json_util 21977dd8813SKowalski, Kamil } // namespace redfish 220