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 18*c94ad49bSEd Tanous #include <http_request.h> 19*c94ad49bSEd Tanous #include <http_response.h> 2077dd8813SKowalski, Kamil 219712f8acSEd Tanous #include <bitset> 229712f8acSEd Tanous #include <error_messages.hpp> 231abe55efSEd Tanous #include <nlohmann/json.hpp> 240627a2c7SEd Tanous 251abe55efSEd Tanous namespace redfish 261abe55efSEd Tanous { 271abe55efSEd Tanous 281abe55efSEd Tanous namespace json_util 291abe55efSEd Tanous { 3077dd8813SKowalski, Kamil 3177dd8813SKowalski, Kamil /** 3277dd8813SKowalski, Kamil * @brief Processes request to extract JSON from its body. If it fails, adds 3377dd8813SKowalski, Kamil * MalformedJSON message to response and ends it. 3477dd8813SKowalski, Kamil * 3577dd8813SKowalski, Kamil * @param[io] res Response object 3677dd8813SKowalski, Kamil * @param[in] req Request object 3777dd8813SKowalski, Kamil * @param[out] reqJson JSON object extracted from request's body 3877dd8813SKowalski, Kamil * 3977dd8813SKowalski, Kamil * @return true if JSON is valid, false when JSON is invalid and response has 4077dd8813SKowalski, Kamil * been filled with message and ended. 4177dd8813SKowalski, Kamil */ 4255c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 4377dd8813SKowalski, Kamil nlohmann::json& reqJson); 449712f8acSEd Tanous namespace details 459712f8acSEd Tanous { 46771cfa0fSJason M. Bills 47771cfa0fSJason M. Bills template <typename Type> struct is_optional : std::false_type 489712f8acSEd Tanous { 499712f8acSEd Tanous }; 509712f8acSEd Tanous 51771cfa0fSJason M. Bills template <typename Type> 52a24526dcSEd Tanous struct is_optional<std::optional<Type>> : std::true_type 539712f8acSEd Tanous { 549712f8acSEd Tanous }; 559712f8acSEd Tanous 56771cfa0fSJason M. Bills template <typename Type> 57771cfa0fSJason M. Bills constexpr bool is_optional_v = is_optional<Type>::value; 58771cfa0fSJason M. Bills 59b1556427SEd Tanous template <typename Type> struct is_vector : std::false_type 60b1556427SEd Tanous { 61b1556427SEd Tanous }; 62b1556427SEd Tanous 63b1556427SEd Tanous template <typename Type> struct is_vector<std::vector<Type>> : std::true_type 64b1556427SEd Tanous { 65b1556427SEd Tanous }; 66b1556427SEd Tanous 67b1556427SEd Tanous template <typename Type> constexpr bool is_vector_v = is_vector<Type>::value; 68b1556427SEd Tanous 69318226c2SJames Feist template <typename Type> struct is_std_array : std::false_type 70318226c2SJames Feist { 71318226c2SJames Feist }; 72318226c2SJames Feist 73318226c2SJames Feist template <typename Type, std::size_t size> 74318226c2SJames Feist struct is_std_array<std::array<Type, size>> : std::true_type 75318226c2SJames Feist { 76318226c2SJames Feist }; 77318226c2SJames Feist 78318226c2SJames Feist template <typename Type> 79318226c2SJames Feist constexpr bool is_std_array_v = is_std_array<Type>::value; 80318226c2SJames Feist 81a6acbb31SJames Feist template <typename ToType, typename FromType> 82ee344e0fSEd Tanous bool checkRange(const FromType& from, const std::string& key, 83a6acbb31SJames Feist nlohmann::json& jsonValue, crow::Response& res) 84a6acbb31SJames Feist { 85ee344e0fSEd Tanous if (from > std::numeric_limits<ToType>::max()) 86a6acbb31SJames Feist { 87a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 88a6acbb31SJames Feist << " was greater than max: " << __PRETTY_FUNCTION__; 89a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 90a6acbb31SJames Feist return false; 91a6acbb31SJames Feist } 92ee344e0fSEd Tanous if (from < std::numeric_limits<ToType>::lowest()) 93a6acbb31SJames Feist { 94a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 95a6acbb31SJames Feist << " was less than min: " << __PRETTY_FUNCTION__; 96a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 97a6acbb31SJames Feist return false; 98a6acbb31SJames Feist } 99a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 100a6acbb31SJames Feist { 101ee344e0fSEd Tanous if (std::isnan(from)) 102a6acbb31SJames Feist { 103a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN"; 104a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 105a6acbb31SJames Feist return false; 106a6acbb31SJames Feist } 107a6acbb31SJames Feist } 108a6acbb31SJames Feist 109a6acbb31SJames Feist return true; 110a6acbb31SJames Feist } 111a6acbb31SJames Feist 112771cfa0fSJason M. Bills template <typename Type> 11341352c24SSantosh Puranik bool unpackValue(nlohmann::json& jsonValue, const std::string& key, 114771cfa0fSJason M. Bills crow::Response& res, Type& value) 115771cfa0fSJason M. Bills { 11641352c24SSantosh Puranik bool ret = true; 11741352c24SSantosh Puranik 118a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 119771cfa0fSJason M. Bills { 120a6acbb31SJames Feist double helper = 0; 121a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 122771cfa0fSJason M. Bills 123771cfa0fSJason M. Bills if (jsonPtr == nullptr) 124771cfa0fSJason M. Bills { 125a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 126a6acbb31SJames Feist if (intPtr != nullptr) 127771cfa0fSJason M. Bills { 128a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 129a6acbb31SJames Feist jsonPtr = &helper; 130771cfa0fSJason M. Bills } 131a6acbb31SJames Feist } 132ee344e0fSEd Tanous if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 133771cfa0fSJason M. Bills { 13441352c24SSantosh Puranik return false; 135771cfa0fSJason M. Bills } 136771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 137771cfa0fSJason M. Bills } 138a6acbb31SJames Feist 139a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 140a6acbb31SJames Feist { 141a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 142271584abSEd Tanous if (jsonPtr == nullptr) 143271584abSEd Tanous { 144271584abSEd Tanous return false; 145271584abSEd Tanous } 146ee344e0fSEd Tanous if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 147a6acbb31SJames Feist { 14841352c24SSantosh Puranik return false; 149a6acbb31SJames Feist } 150a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 151a6acbb31SJames Feist } 152a6acbb31SJames Feist 1538102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1548102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 155a6acbb31SJames Feist { 156a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 157271584abSEd Tanous if (jsonPtr == nullptr) 158271584abSEd Tanous { 159271584abSEd Tanous return false; 160271584abSEd Tanous } 161ee344e0fSEd Tanous if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 162a6acbb31SJames Feist { 16341352c24SSantosh Puranik return false; 164a6acbb31SJames Feist } 165a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 166a6acbb31SJames Feist } 167a6acbb31SJames Feist 168771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 169771cfa0fSJason M. Bills { 170771cfa0fSJason M. Bills value.emplace(); 17141352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 17241352c24SSantosh Puranik *value) && 17341352c24SSantosh Puranik ret; 174771cfa0fSJason M. Bills } 1750627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1760627a2c7SEd Tanous { 1770627a2c7SEd Tanous // Must be a complex type. Simple types (int string etc) should be 1780627a2c7SEd Tanous // unpacked directly 1798ebc91f6SEd Tanous if (!jsonValue.is_object() && !jsonValue.is_array() && 1808ebc91f6SEd Tanous !jsonValue.is_null()) 1810627a2c7SEd Tanous { 1820627a2c7SEd Tanous messages::propertyValueTypeError(res, jsonValue.dump(), key); 18341352c24SSantosh Puranik return false; 1840627a2c7SEd Tanous } 1850627a2c7SEd Tanous 1860627a2c7SEd Tanous value = std::move(jsonValue); 1870627a2c7SEd Tanous } 188318226c2SJames Feist else if constexpr (is_std_array_v<Type>) 189318226c2SJames Feist { 190318226c2SJames Feist if (!jsonValue.is_array()) 191318226c2SJames Feist { 192318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 19341352c24SSantosh Puranik return false; 194318226c2SJames Feist } 195318226c2SJames Feist if (jsonValue.size() != value.size()) 196318226c2SJames Feist { 197318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 19841352c24SSantosh Puranik return false; 199318226c2SJames Feist } 200318226c2SJames Feist size_t index = 0; 201318226c2SJames Feist for (const auto& val : jsonValue.items()) 202318226c2SJames Feist { 20341352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 20441352c24SSantosh Puranik value[index++]) && 20541352c24SSantosh Puranik ret; 206318226c2SJames Feist } 207318226c2SJames Feist } 208b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 209b1556427SEd Tanous { 210b1556427SEd Tanous if (!jsonValue.is_array()) 211b1556427SEd Tanous { 212b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 21341352c24SSantosh Puranik return false; 214b1556427SEd Tanous } 215b1556427SEd Tanous 216b1556427SEd Tanous for (const auto& val : jsonValue.items()) 217b1556427SEd Tanous { 218b1556427SEd Tanous value.emplace_back(); 21941352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 22041352c24SSantosh Puranik value.back()) && 22141352c24SSantosh Puranik ret; 222b1556427SEd Tanous } 223b1556427SEd Tanous } 224771cfa0fSJason M. Bills else 225771cfa0fSJason M. Bills { 226771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 227771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 228771cfa0fSJason M. Bills if (jsonPtr == nullptr) 229771cfa0fSJason M. Bills { 230771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 231771cfa0fSJason M. Bills << "Value for key " << key 232771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 233771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 23441352c24SSantosh Puranik return false; 235771cfa0fSJason M. Bills } 236771cfa0fSJason M. Bills value = std::move(*jsonPtr); 237771cfa0fSJason M. Bills } 23841352c24SSantosh Puranik return ret; 239771cfa0fSJason M. Bills } 240771cfa0fSJason M. Bills 2419712f8acSEd Tanous template <size_t Count, size_t Index> 24241352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2439712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 2449712f8acSEd Tanous { 2459712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 246a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 24741352c24SSantosh Puranik return false; 2489712f8acSEd Tanous } 2499712f8acSEd Tanous 2509712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 2519712f8acSEd Tanous typename... UnpackTypes> 25241352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2539712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 2549712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 2559712f8acSEd Tanous UnpackTypes&... in) 2569712f8acSEd Tanous { 25741352c24SSantosh Puranik bool ret = true; 2589712f8acSEd Tanous if (key != keyToCheck) 2599712f8acSEd Tanous { 26041352c24SSantosh Puranik ret = readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, 26141352c24SSantosh Puranik in...) && 26241352c24SSantosh Puranik ret; 26341352c24SSantosh Puranik return ret; 2649712f8acSEd Tanous } 2659712f8acSEd Tanous 2669712f8acSEd Tanous handled.set(Index); 2679712f8acSEd Tanous 26841352c24SSantosh Puranik return unpackValue<ValueType>(jsonValue, key, res, valueToFill) && ret; 2699712f8acSEd Tanous } 2709712f8acSEd Tanous 2719712f8acSEd Tanous template <size_t Index = 0, size_t Count> 27241352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res) 2739712f8acSEd Tanous { 27441352c24SSantosh Puranik return true; 2759712f8acSEd Tanous } 2769712f8acSEd Tanous 2779712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 2789712f8acSEd Tanous typename... UnpackTypes> 27941352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res, 2809712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 2819712f8acSEd Tanous { 28241352c24SSantosh Puranik bool ret = true; 283771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 2849712f8acSEd Tanous { 28541352c24SSantosh Puranik ret = false; 286a08b46ccSJason M. Bills messages::propertyMissing(res, key); 2879712f8acSEd Tanous } 28841352c24SSantosh Puranik return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret; 2899712f8acSEd Tanous } 2909712f8acSEd Tanous } // namespace details 2919712f8acSEd Tanous 2929712f8acSEd Tanous template <typename... UnpackTypes> 2930627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 2949712f8acSEd Tanous UnpackTypes&... in) 2959712f8acSEd Tanous { 29641352c24SSantosh Puranik bool result = true; 2979712f8acSEd Tanous if (!jsonRequest.is_object()) 2989712f8acSEd Tanous { 2999712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 300f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 3019712f8acSEd Tanous return false; 3029712f8acSEd Tanous } 3039712f8acSEd Tanous 3049712f8acSEd Tanous if (jsonRequest.empty()) 3059712f8acSEd Tanous { 3069712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 307f12894f8SJason M. Bills messages::emptyJSON(res); 3089712f8acSEd Tanous return false; 3099712f8acSEd Tanous } 3109712f8acSEd Tanous 3119712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 3129712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 3139712f8acSEd Tanous { 31441352c24SSantosh Puranik result = 3159712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 31641352c24SSantosh Puranik item.key(), item.value(), res, handled, key, in...) && 31741352c24SSantosh Puranik result; 3189712f8acSEd Tanous } 3199712f8acSEd Tanous 32041352c24SSantosh Puranik BMCWEB_LOG_DEBUG << "JSON result is: " << result; 3219712f8acSEd Tanous 32241352c24SSantosh Puranik return details::handleMissing(handled, res, key, in...) && result; 3239712f8acSEd Tanous } 32477dd8813SKowalski, Kamil 3250627a2c7SEd Tanous template <typename... UnpackTypes> 3260627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 3270627a2c7SEd Tanous UnpackTypes&... in) 3280627a2c7SEd Tanous { 3290627a2c7SEd Tanous nlohmann::json jsonRequest; 3300627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 3310627a2c7SEd Tanous { 3320627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 3330627a2c7SEd Tanous return false; 3340627a2c7SEd Tanous } 3350627a2c7SEd Tanous return readJson(jsonRequest, res, key, in...); 3360627a2c7SEd Tanous } 3370627a2c7SEd Tanous 33877dd8813SKowalski, Kamil } // namespace json_util 33977dd8813SKowalski, Kamil } // namespace redfish 340