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> 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*>(); 142ee344e0fSEd Tanous if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 143a6acbb31SJames Feist { 14441352c24SSantosh Puranik return false; 145a6acbb31SJames Feist } 146a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 147a6acbb31SJames Feist } 148a6acbb31SJames Feist 1498102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1508102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 151a6acbb31SJames Feist { 152a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 153ee344e0fSEd Tanous if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 154a6acbb31SJames Feist { 15541352c24SSantosh Puranik return false; 156a6acbb31SJames Feist } 157a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 158a6acbb31SJames Feist } 159a6acbb31SJames Feist 160771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 161771cfa0fSJason M. Bills { 162771cfa0fSJason M. Bills value.emplace(); 16341352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 16441352c24SSantosh Puranik *value) && 16541352c24SSantosh Puranik ret; 166771cfa0fSJason M. Bills } 1670627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1680627a2c7SEd Tanous { 1690627a2c7SEd Tanous // Must be a complex type. Simple types (int string etc) should be 1700627a2c7SEd Tanous // unpacked directly 171*8ebc91f6SEd Tanous if (!jsonValue.is_object() && !jsonValue.is_array() && 172*8ebc91f6SEd Tanous !jsonValue.is_null()) 1730627a2c7SEd Tanous { 1740627a2c7SEd Tanous messages::propertyValueTypeError(res, jsonValue.dump(), key); 17541352c24SSantosh Puranik return false; 1760627a2c7SEd Tanous } 1770627a2c7SEd Tanous 1780627a2c7SEd Tanous value = std::move(jsonValue); 1790627a2c7SEd Tanous } 180318226c2SJames Feist else if constexpr (is_std_array_v<Type>) 181318226c2SJames Feist { 182318226c2SJames Feist if (!jsonValue.is_array()) 183318226c2SJames Feist { 184318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 18541352c24SSantosh Puranik return false; 186318226c2SJames Feist } 187318226c2SJames Feist if (jsonValue.size() != value.size()) 188318226c2SJames Feist { 189318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 19041352c24SSantosh Puranik return false; 191318226c2SJames Feist } 192318226c2SJames Feist size_t index = 0; 193318226c2SJames Feist for (const auto& val : jsonValue.items()) 194318226c2SJames Feist { 19541352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 19641352c24SSantosh Puranik value[index++]) && 19741352c24SSantosh Puranik ret; 198318226c2SJames Feist } 199318226c2SJames Feist } 200b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 201b1556427SEd Tanous { 202b1556427SEd Tanous if (!jsonValue.is_array()) 203b1556427SEd Tanous { 204b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 20541352c24SSantosh Puranik return false; 206b1556427SEd Tanous } 207b1556427SEd Tanous 208b1556427SEd Tanous for (const auto& val : jsonValue.items()) 209b1556427SEd Tanous { 210b1556427SEd Tanous value.emplace_back(); 21141352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 21241352c24SSantosh Puranik value.back()) && 21341352c24SSantosh Puranik ret; 214b1556427SEd Tanous } 215b1556427SEd Tanous } 216771cfa0fSJason M. Bills else 217771cfa0fSJason M. Bills { 218771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 219771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 220771cfa0fSJason M. Bills if (jsonPtr == nullptr) 221771cfa0fSJason M. Bills { 222771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 223771cfa0fSJason M. Bills << "Value for key " << key 224771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 225771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 22641352c24SSantosh Puranik return false; 227771cfa0fSJason M. Bills } 228771cfa0fSJason M. Bills value = std::move(*jsonPtr); 229771cfa0fSJason M. Bills } 23041352c24SSantosh Puranik return ret; 231771cfa0fSJason M. Bills } 232771cfa0fSJason M. Bills 2339712f8acSEd Tanous template <size_t Count, size_t Index> 23441352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2359712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 2369712f8acSEd Tanous { 2379712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 238a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 23941352c24SSantosh Puranik return false; 2409712f8acSEd Tanous } 2419712f8acSEd Tanous 2429712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 2439712f8acSEd Tanous typename... UnpackTypes> 24441352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2459712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 2469712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 2479712f8acSEd Tanous UnpackTypes&... in) 2489712f8acSEd Tanous { 24941352c24SSantosh Puranik bool ret = true; 2509712f8acSEd Tanous if (key != keyToCheck) 2519712f8acSEd Tanous { 25241352c24SSantosh Puranik ret = readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, 25341352c24SSantosh Puranik in...) && 25441352c24SSantosh Puranik ret; 25541352c24SSantosh Puranik return ret; 2569712f8acSEd Tanous } 2579712f8acSEd Tanous 2589712f8acSEd Tanous handled.set(Index); 2599712f8acSEd Tanous 26041352c24SSantosh Puranik return unpackValue<ValueType>(jsonValue, key, res, valueToFill) && ret; 2619712f8acSEd Tanous } 2629712f8acSEd Tanous 2639712f8acSEd Tanous template <size_t Index = 0, size_t Count> 26441352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res) 2659712f8acSEd Tanous { 26641352c24SSantosh Puranik return true; 2679712f8acSEd Tanous } 2689712f8acSEd Tanous 2699712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 2709712f8acSEd Tanous typename... UnpackTypes> 27141352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res, 2729712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 2739712f8acSEd Tanous { 27441352c24SSantosh Puranik bool ret = true; 275771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 2769712f8acSEd Tanous { 27741352c24SSantosh Puranik ret = false; 278a08b46ccSJason M. Bills messages::propertyMissing(res, key); 2799712f8acSEd Tanous } 28041352c24SSantosh Puranik return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret; 2819712f8acSEd Tanous } 2829712f8acSEd Tanous } // namespace details 2839712f8acSEd Tanous 2849712f8acSEd Tanous template <typename... UnpackTypes> 2850627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 2869712f8acSEd Tanous UnpackTypes&... in) 2879712f8acSEd Tanous { 28841352c24SSantosh Puranik bool result = true; 2899712f8acSEd Tanous if (!jsonRequest.is_object()) 2909712f8acSEd Tanous { 2919712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 292f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 2939712f8acSEd Tanous return false; 2949712f8acSEd Tanous } 2959712f8acSEd Tanous 2969712f8acSEd Tanous if (jsonRequest.empty()) 2979712f8acSEd Tanous { 2989712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 299f12894f8SJason M. Bills messages::emptyJSON(res); 3009712f8acSEd Tanous return false; 3019712f8acSEd Tanous } 3029712f8acSEd Tanous 3039712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 3049712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 3059712f8acSEd Tanous { 30641352c24SSantosh Puranik result = 3079712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 30841352c24SSantosh Puranik item.key(), item.value(), res, handled, key, in...) && 30941352c24SSantosh Puranik result; 3109712f8acSEd Tanous } 3119712f8acSEd Tanous 31241352c24SSantosh Puranik BMCWEB_LOG_DEBUG << "JSON result is: " << result; 3139712f8acSEd Tanous 31441352c24SSantosh Puranik return details::handleMissing(handled, res, key, in...) && result; 3159712f8acSEd Tanous } 31677dd8813SKowalski, Kamil 3170627a2c7SEd Tanous template <typename... UnpackTypes> 3180627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 3190627a2c7SEd Tanous UnpackTypes&... in) 3200627a2c7SEd Tanous { 3210627a2c7SEd Tanous nlohmann::json jsonRequest; 3220627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 3230627a2c7SEd Tanous { 3240627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 3250627a2c7SEd Tanous return false; 3260627a2c7SEd Tanous } 3270627a2c7SEd Tanous return readJson(jsonRequest, res, key, in...); 3280627a2c7SEd Tanous } 3290627a2c7SEd Tanous 33077dd8813SKowalski, Kamil } // namespace json_util 33177dd8813SKowalski, Kamil } // namespace redfish 332