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> 24*0627a2c7SEd 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 69771cfa0fSJason M. Bills template <typename Type> 70771cfa0fSJason M. Bills void unpackValue(nlohmann::json& jsonValue, const std::string& key, 71771cfa0fSJason M. Bills crow::Response& res, Type& value) 72771cfa0fSJason M. Bills { 73771cfa0fSJason M. Bills if constexpr (std::is_arithmetic_v<Type>) 74771cfa0fSJason M. Bills { 75771cfa0fSJason M. Bills using NumType = 76771cfa0fSJason M. Bills std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 77771cfa0fSJason M. Bills 78771cfa0fSJason M. Bills NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 79771cfa0fSJason M. Bills if (jsonPtr == nullptr) 80771cfa0fSJason M. Bills { 81771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 82771cfa0fSJason M. Bills << "Value for key " << key 83771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 84771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 85771cfa0fSJason M. Bills return; 86771cfa0fSJason M. Bills } 87771cfa0fSJason M. Bills if (*jsonPtr > std::numeric_limits<Type>::max()) 88771cfa0fSJason M. Bills { 89771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 90771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 91771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 92771cfa0fSJason M. Bills return; 93771cfa0fSJason M. Bills } 94771cfa0fSJason M. Bills if (*jsonPtr < std::numeric_limits<Type>::min()) 95771cfa0fSJason M. Bills { 96771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 97771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 98771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 99771cfa0fSJason M. Bills return; 100771cfa0fSJason M. Bills } 101771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 102771cfa0fSJason M. Bills } 103771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 104771cfa0fSJason M. Bills { 105771cfa0fSJason M. Bills value.emplace(); 106771cfa0fSJason M. Bills unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 107771cfa0fSJason M. Bills } 108*0627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 109*0627a2c7SEd Tanous { 110*0627a2c7SEd Tanous // Must be a complex type. Simple types (int string etc) should be 111*0627a2c7SEd Tanous // unpacked directly 112*0627a2c7SEd Tanous if (!jsonValue.is_object() && !jsonValue.is_array()) 113*0627a2c7SEd Tanous { 114*0627a2c7SEd Tanous messages::propertyValueTypeError(res, jsonValue.dump(), key); 115*0627a2c7SEd Tanous return; 116*0627a2c7SEd Tanous } 117*0627a2c7SEd Tanous 118*0627a2c7SEd Tanous value = std::move(jsonValue); 119*0627a2c7SEd Tanous } 120b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 121b1556427SEd Tanous { 122b1556427SEd Tanous if (!jsonValue.is_array()) 123b1556427SEd Tanous { 124b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 125b1556427SEd Tanous return; 126b1556427SEd Tanous } 127b1556427SEd Tanous 128b1556427SEd Tanous for (const auto& val : jsonValue.items()) 129b1556427SEd Tanous { 130b1556427SEd Tanous value.emplace_back(); 131b1556427SEd Tanous unpackValue<typename Type::value_type>(val.value(), key, res, 132b1556427SEd Tanous value.back()); 133b1556427SEd Tanous } 134b1556427SEd Tanous } 135771cfa0fSJason M. Bills else 136771cfa0fSJason M. Bills { 137771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 138771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 139771cfa0fSJason M. Bills if (jsonPtr == nullptr) 140771cfa0fSJason M. Bills { 141771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 142771cfa0fSJason M. Bills << "Value for key " << key 143771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 144771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 145771cfa0fSJason M. Bills return; 146771cfa0fSJason M. Bills } 147771cfa0fSJason M. Bills value = std::move(*jsonPtr); 148771cfa0fSJason M. Bills } 149771cfa0fSJason M. Bills } 150771cfa0fSJason M. Bills 1519712f8acSEd Tanous template <size_t Count, size_t Index> 1529712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1539712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 1549712f8acSEd Tanous { 1559712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 156a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 1579712f8acSEd Tanous } 1589712f8acSEd Tanous 1599712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 1609712f8acSEd Tanous typename... UnpackTypes> 1619712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1629712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 1639712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 1649712f8acSEd Tanous UnpackTypes&... in) 1659712f8acSEd Tanous { 1669712f8acSEd Tanous if (key != keyToCheck) 1679712f8acSEd Tanous { 1689712f8acSEd Tanous readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 1699712f8acSEd Tanous return; 1709712f8acSEd Tanous } 1719712f8acSEd Tanous 1729712f8acSEd Tanous handled.set(Index); 1739712f8acSEd Tanous 174771cfa0fSJason M. Bills unpackValue<ValueType>(jsonValue, key, res, valueToFill); 1759712f8acSEd Tanous } 1769712f8acSEd Tanous 1779712f8acSEd Tanous template <size_t Index = 0, size_t Count> 1789712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res) 1799712f8acSEd Tanous { 1809712f8acSEd Tanous } 1819712f8acSEd Tanous 1829712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 1839712f8acSEd Tanous typename... UnpackTypes> 1849712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res, 1859712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 1869712f8acSEd Tanous { 187771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 1889712f8acSEd Tanous { 189a08b46ccSJason M. Bills messages::propertyMissing(res, key); 1909712f8acSEd Tanous } 1919712f8acSEd Tanous details::handleMissing<Index + 1, Count>(handled, res, in...); 1929712f8acSEd Tanous } 1939712f8acSEd Tanous } // namespace details 1949712f8acSEd Tanous 1959712f8acSEd Tanous template <typename... UnpackTypes> 196*0627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 1979712f8acSEd Tanous UnpackTypes&... in) 1989712f8acSEd Tanous { 1999712f8acSEd Tanous if (!jsonRequest.is_object()) 2009712f8acSEd Tanous { 2019712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 202f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 2039712f8acSEd Tanous return false; 2049712f8acSEd Tanous } 2059712f8acSEd Tanous 2069712f8acSEd Tanous if (jsonRequest.empty()) 2079712f8acSEd Tanous { 2089712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 209f12894f8SJason M. Bills messages::emptyJSON(res); 2109712f8acSEd Tanous return false; 2119712f8acSEd Tanous } 2129712f8acSEd Tanous 2139712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 2149712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 2159712f8acSEd Tanous { 2169712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 2179712f8acSEd Tanous item.key(), item.value(), res, handled, key, in...); 2189712f8acSEd Tanous } 2199712f8acSEd Tanous 2209712f8acSEd Tanous details::handleMissing(handled, res, key, in...); 2219712f8acSEd Tanous 2229712f8acSEd Tanous return res.result() == boost::beast::http::status::ok; 2239712f8acSEd Tanous } 22477dd8813SKowalski, Kamil 225*0627a2c7SEd Tanous template <typename... UnpackTypes> 226*0627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 227*0627a2c7SEd Tanous UnpackTypes&... in) 228*0627a2c7SEd Tanous { 229*0627a2c7SEd Tanous nlohmann::json jsonRequest; 230*0627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 231*0627a2c7SEd Tanous { 232*0627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 233*0627a2c7SEd Tanous return false; 234*0627a2c7SEd Tanous } 235*0627a2c7SEd Tanous return readJson(jsonRequest, res, key, in...); 236*0627a2c7SEd Tanous } 237*0627a2c7SEd Tanous 23877dd8813SKowalski, Kamil } // namespace json_util 23977dd8813SKowalski, Kamil } // namespace redfish 240