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> 82a6acbb31SJames Feist bool checkRange(const FromType* from, const std::string& key, 83a6acbb31SJames Feist nlohmann::json& jsonValue, crow::Response& res) 84a6acbb31SJames Feist { 85a6acbb31SJames Feist if (from == nullptr) 86a6acbb31SJames Feist { 87a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 88a6acbb31SJames Feist << " was incorrect type: " << __PRETTY_FUNCTION__; 89a6acbb31SJames Feist messages::propertyValueTypeError(res, jsonValue.dump(), key); 90a6acbb31SJames Feist return false; 91a6acbb31SJames Feist } 92a6acbb31SJames Feist 93a6acbb31SJames Feist if (*from > std::numeric_limits<ToType>::max()) 94a6acbb31SJames Feist { 95a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 96a6acbb31SJames Feist << " was greater than max: " << __PRETTY_FUNCTION__; 97a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 98a6acbb31SJames Feist return false; 99a6acbb31SJames Feist } 100a6acbb31SJames Feist if (*from < std::numeric_limits<ToType>::lowest()) 101a6acbb31SJames Feist { 102a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 103a6acbb31SJames Feist << " was less than min: " << __PRETTY_FUNCTION__; 104a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 105a6acbb31SJames Feist return false; 106a6acbb31SJames Feist } 107a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 108a6acbb31SJames Feist { 109a6acbb31SJames Feist if (std::isnan(*from)) 110a6acbb31SJames Feist { 111a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN"; 112a6acbb31SJames Feist messages::propertyValueNotInList(res, jsonValue.dump(), key); 113a6acbb31SJames Feist return false; 114a6acbb31SJames Feist } 115a6acbb31SJames Feist } 116a6acbb31SJames Feist 117a6acbb31SJames Feist return true; 118a6acbb31SJames Feist } 119a6acbb31SJames Feist 120771cfa0fSJason M. Bills template <typename Type> 121*41352c24SSantosh Puranik bool unpackValue(nlohmann::json& jsonValue, const std::string& key, 122771cfa0fSJason M. Bills crow::Response& res, Type& value) 123771cfa0fSJason M. Bills { 124*41352c24SSantosh Puranik bool ret = true; 125*41352c24SSantosh Puranik 126a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 127771cfa0fSJason M. Bills { 128a6acbb31SJames Feist double helper = 0; 129a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 130771cfa0fSJason M. Bills 131771cfa0fSJason M. Bills if (jsonPtr == nullptr) 132771cfa0fSJason M. Bills { 133a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 134a6acbb31SJames Feist if (intPtr != nullptr) 135771cfa0fSJason M. Bills { 136a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 137a6acbb31SJames Feist jsonPtr = &helper; 138771cfa0fSJason M. Bills } 139a6acbb31SJames Feist } 140a6acbb31SJames Feist if (!checkRange<Type>(jsonPtr, key, jsonValue, res)) 141771cfa0fSJason M. Bills { 142*41352c24SSantosh Puranik return false; 143771cfa0fSJason M. Bills } 144771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 145771cfa0fSJason M. Bills } 146a6acbb31SJames Feist 147a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 148a6acbb31SJames Feist { 149a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 150a6acbb31SJames Feist if (!checkRange<Type>(jsonPtr, key, jsonValue, res)) 151a6acbb31SJames Feist { 152*41352c24SSantosh Puranik return false; 153a6acbb31SJames Feist } 154a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 155a6acbb31SJames Feist } 156a6acbb31SJames Feist 1578102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1588102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 159a6acbb31SJames Feist { 160a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 161a6acbb31SJames Feist if (!checkRange<Type>(jsonPtr, key, jsonValue, res)) 162a6acbb31SJames Feist { 163*41352c24SSantosh 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(); 171*41352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 172*41352c24SSantosh Puranik *value) && 173*41352c24SSantosh 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 1790627a2c7SEd Tanous if (!jsonValue.is_object() && !jsonValue.is_array()) 1800627a2c7SEd Tanous { 1810627a2c7SEd Tanous messages::propertyValueTypeError(res, jsonValue.dump(), key); 182*41352c24SSantosh Puranik return false; 1830627a2c7SEd Tanous } 1840627a2c7SEd Tanous 1850627a2c7SEd Tanous value = std::move(jsonValue); 1860627a2c7SEd Tanous } 187318226c2SJames Feist else if constexpr (is_std_array_v<Type>) 188318226c2SJames Feist { 189318226c2SJames Feist if (!jsonValue.is_array()) 190318226c2SJames Feist { 191318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 192*41352c24SSantosh Puranik return false; 193318226c2SJames Feist } 194318226c2SJames Feist if (jsonValue.size() != value.size()) 195318226c2SJames Feist { 196318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 197*41352c24SSantosh Puranik return false; 198318226c2SJames Feist } 199318226c2SJames Feist size_t index = 0; 200318226c2SJames Feist for (const auto& val : jsonValue.items()) 201318226c2SJames Feist { 202*41352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 203*41352c24SSantosh Puranik value[index++]) && 204*41352c24SSantosh Puranik ret; 205318226c2SJames Feist } 206318226c2SJames Feist } 207b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 208b1556427SEd Tanous { 209b1556427SEd Tanous if (!jsonValue.is_array()) 210b1556427SEd Tanous { 211b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 212*41352c24SSantosh Puranik return false; 213b1556427SEd Tanous } 214b1556427SEd Tanous 215b1556427SEd Tanous for (const auto& val : jsonValue.items()) 216b1556427SEd Tanous { 217b1556427SEd Tanous value.emplace_back(); 218*41352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 219*41352c24SSantosh Puranik value.back()) && 220*41352c24SSantosh Puranik ret; 221b1556427SEd Tanous } 222b1556427SEd Tanous } 223771cfa0fSJason M. Bills else 224771cfa0fSJason M. Bills { 225771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 226771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 227771cfa0fSJason M. Bills if (jsonPtr == nullptr) 228771cfa0fSJason M. Bills { 229771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 230771cfa0fSJason M. Bills << "Value for key " << key 231771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 232771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 233*41352c24SSantosh Puranik return false; 234771cfa0fSJason M. Bills } 235771cfa0fSJason M. Bills value = std::move(*jsonPtr); 236771cfa0fSJason M. Bills } 237*41352c24SSantosh Puranik return ret; 238771cfa0fSJason M. Bills } 239771cfa0fSJason M. Bills 2409712f8acSEd Tanous template <size_t Count, size_t Index> 241*41352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2429712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 2439712f8acSEd Tanous { 2449712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 245a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 246*41352c24SSantosh Puranik return false; 2479712f8acSEd Tanous } 2489712f8acSEd Tanous 2499712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 2509712f8acSEd Tanous typename... UnpackTypes> 251*41352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 2529712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 2539712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 2549712f8acSEd Tanous UnpackTypes&... in) 2559712f8acSEd Tanous { 256*41352c24SSantosh Puranik bool ret = true; 2579712f8acSEd Tanous if (key != keyToCheck) 2589712f8acSEd Tanous { 259*41352c24SSantosh Puranik ret = readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, 260*41352c24SSantosh Puranik in...) && 261*41352c24SSantosh Puranik ret; 262*41352c24SSantosh Puranik return ret; 2639712f8acSEd Tanous } 2649712f8acSEd Tanous 2659712f8acSEd Tanous handled.set(Index); 2669712f8acSEd Tanous 267*41352c24SSantosh Puranik return unpackValue<ValueType>(jsonValue, key, res, valueToFill) && ret; 2689712f8acSEd Tanous } 2699712f8acSEd Tanous 2709712f8acSEd Tanous template <size_t Index = 0, size_t Count> 271*41352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res) 2729712f8acSEd Tanous { 273*41352c24SSantosh Puranik return true; 2749712f8acSEd Tanous } 2759712f8acSEd Tanous 2769712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 2779712f8acSEd Tanous typename... UnpackTypes> 278*41352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res, 2799712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 2809712f8acSEd Tanous { 281*41352c24SSantosh Puranik bool ret = true; 282771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 2839712f8acSEd Tanous { 284*41352c24SSantosh Puranik ret = false; 285a08b46ccSJason M. Bills messages::propertyMissing(res, key); 2869712f8acSEd Tanous } 287*41352c24SSantosh Puranik return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret; 2889712f8acSEd Tanous } 2899712f8acSEd Tanous } // namespace details 2909712f8acSEd Tanous 2919712f8acSEd Tanous template <typename... UnpackTypes> 2920627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 2939712f8acSEd Tanous UnpackTypes&... in) 2949712f8acSEd Tanous { 295*41352c24SSantosh Puranik bool result = true; 2969712f8acSEd Tanous if (!jsonRequest.is_object()) 2979712f8acSEd Tanous { 2989712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 299f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 3009712f8acSEd Tanous return false; 3019712f8acSEd Tanous } 3029712f8acSEd Tanous 3039712f8acSEd Tanous if (jsonRequest.empty()) 3049712f8acSEd Tanous { 3059712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 306f12894f8SJason M. Bills messages::emptyJSON(res); 3079712f8acSEd Tanous return false; 3089712f8acSEd Tanous } 3099712f8acSEd Tanous 3109712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 3119712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 3129712f8acSEd Tanous { 313*41352c24SSantosh Puranik result = 3149712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 315*41352c24SSantosh Puranik item.key(), item.value(), res, handled, key, in...) && 316*41352c24SSantosh Puranik result; 3179712f8acSEd Tanous } 3189712f8acSEd Tanous 319*41352c24SSantosh Puranik BMCWEB_LOG_DEBUG << "JSON result is: " << result; 3209712f8acSEd Tanous 321*41352c24SSantosh Puranik return details::handleMissing(handled, res, key, in...) && result; 3229712f8acSEd Tanous } 32377dd8813SKowalski, Kamil 3240627a2c7SEd Tanous template <typename... UnpackTypes> 3250627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 3260627a2c7SEd Tanous UnpackTypes&... in) 3270627a2c7SEd Tanous { 3280627a2c7SEd Tanous nlohmann::json jsonRequest; 3290627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 3300627a2c7SEd Tanous { 3310627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 3320627a2c7SEd Tanous return false; 3330627a2c7SEd Tanous } 3340627a2c7SEd Tanous return readJson(jsonRequest, res, key, in...); 3350627a2c7SEd Tanous } 3360627a2c7SEd Tanous 33777dd8813SKowalski, Kamil } // namespace json_util 33877dd8813SKowalski, Kamil } // namespace redfish 339