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 69*318226c2SJames Feist template <typename Type> struct is_std_array : std::false_type 70*318226c2SJames Feist { 71*318226c2SJames Feist }; 72*318226c2SJames Feist 73*318226c2SJames Feist template <typename Type, std::size_t size> 74*318226c2SJames Feist struct is_std_array<std::array<Type, size>> : std::true_type 75*318226c2SJames Feist { 76*318226c2SJames Feist }; 77*318226c2SJames Feist 78*318226c2SJames Feist template <typename Type> 79*318226c2SJames Feist constexpr bool is_std_array_v = is_std_array<Type>::value; 80*318226c2SJames Feist 81771cfa0fSJason M. Bills template <typename Type> 82771cfa0fSJason M. Bills void unpackValue(nlohmann::json& jsonValue, const std::string& key, 83771cfa0fSJason M. Bills crow::Response& res, Type& value) 84771cfa0fSJason M. Bills { 85771cfa0fSJason M. Bills if constexpr (std::is_arithmetic_v<Type>) 86771cfa0fSJason M. Bills { 87771cfa0fSJason M. Bills using NumType = 88771cfa0fSJason M. Bills std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 89771cfa0fSJason M. Bills 90771cfa0fSJason M. Bills NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 91771cfa0fSJason M. Bills if (jsonPtr == nullptr) 92771cfa0fSJason M. Bills { 93771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 94771cfa0fSJason M. Bills << "Value for key " << key 95771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 96771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 97771cfa0fSJason M. Bills return; 98771cfa0fSJason M. Bills } 99771cfa0fSJason M. Bills if (*jsonPtr > std::numeric_limits<Type>::max()) 100771cfa0fSJason M. Bills { 101771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 102771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 103771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 104771cfa0fSJason M. Bills return; 105771cfa0fSJason M. Bills } 106771cfa0fSJason M. Bills if (*jsonPtr < std::numeric_limits<Type>::min()) 107771cfa0fSJason M. Bills { 108771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG << "Value for key " << key 109771cfa0fSJason M. Bills << " was out of range: " << jsonValue.type_name(); 110771cfa0fSJason M. Bills messages::propertyValueNotInList(res, jsonValue.dump(), key); 111771cfa0fSJason M. Bills return; 112771cfa0fSJason M. Bills } 113771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 114771cfa0fSJason M. Bills } 115771cfa0fSJason M. Bills else if constexpr (is_optional_v<Type>) 116771cfa0fSJason M. Bills { 117771cfa0fSJason M. Bills value.emplace(); 118771cfa0fSJason M. Bills unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 119771cfa0fSJason M. Bills } 1200627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1210627a2c7SEd Tanous { 1220627a2c7SEd Tanous // Must be a complex type. Simple types (int string etc) should be 1230627a2c7SEd Tanous // unpacked directly 1240627a2c7SEd Tanous if (!jsonValue.is_object() && !jsonValue.is_array()) 1250627a2c7SEd Tanous { 1260627a2c7SEd Tanous messages::propertyValueTypeError(res, jsonValue.dump(), key); 1270627a2c7SEd Tanous return; 1280627a2c7SEd Tanous } 1290627a2c7SEd Tanous 1300627a2c7SEd Tanous value = std::move(jsonValue); 1310627a2c7SEd Tanous } 132*318226c2SJames Feist else if constexpr (is_std_array_v<Type>) 133*318226c2SJames Feist { 134*318226c2SJames Feist if (!jsonValue.is_array()) 135*318226c2SJames Feist { 136*318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 137*318226c2SJames Feist return; 138*318226c2SJames Feist } 139*318226c2SJames Feist if (jsonValue.size() != value.size()) 140*318226c2SJames Feist { 141*318226c2SJames Feist messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 142*318226c2SJames Feist return; 143*318226c2SJames Feist } 144*318226c2SJames Feist size_t index = 0; 145*318226c2SJames Feist for (const auto& val : jsonValue.items()) 146*318226c2SJames Feist { 147*318226c2SJames Feist unpackValue<typename Type::value_type>(val.value(), key, res, 148*318226c2SJames Feist value[index++]); 149*318226c2SJames Feist } 150*318226c2SJames Feist } 151b1556427SEd Tanous else if constexpr (is_vector_v<Type>) 152b1556427SEd Tanous { 153b1556427SEd Tanous if (!jsonValue.is_array()) 154b1556427SEd Tanous { 155b1556427SEd Tanous messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 156b1556427SEd Tanous return; 157b1556427SEd Tanous } 158b1556427SEd Tanous 159b1556427SEd Tanous for (const auto& val : jsonValue.items()) 160b1556427SEd Tanous { 161b1556427SEd Tanous value.emplace_back(); 162b1556427SEd Tanous unpackValue<typename Type::value_type>(val.value(), key, res, 163b1556427SEd Tanous value.back()); 164b1556427SEd Tanous } 165b1556427SEd Tanous } 166771cfa0fSJason M. Bills else 167771cfa0fSJason M. Bills { 168771cfa0fSJason M. Bills using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 169771cfa0fSJason M. Bills JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 170771cfa0fSJason M. Bills if (jsonPtr == nullptr) 171771cfa0fSJason M. Bills { 172771cfa0fSJason M. Bills BMCWEB_LOG_DEBUG 173771cfa0fSJason M. Bills << "Value for key " << key 174771cfa0fSJason M. Bills << " was incorrect type: " << jsonValue.type_name(); 175771cfa0fSJason M. Bills messages::propertyValueTypeError(res, jsonValue.dump(), key); 176771cfa0fSJason M. Bills return; 177771cfa0fSJason M. Bills } 178771cfa0fSJason M. Bills value = std::move(*jsonPtr); 179771cfa0fSJason M. Bills } 180771cfa0fSJason M. Bills } 181771cfa0fSJason M. Bills 1829712f8acSEd Tanous template <size_t Count, size_t Index> 1839712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1849712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled) 1859712f8acSEd Tanous { 1869712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 187a08b46ccSJason M. Bills messages::propertyUnknown(res, key); 1889712f8acSEd Tanous } 1899712f8acSEd Tanous 1909712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType, 1919712f8acSEd Tanous typename... UnpackTypes> 1929712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 1939712f8acSEd Tanous crow::Response& res, std::bitset<Count>& handled, 1949712f8acSEd Tanous const char* keyToCheck, ValueType& valueToFill, 1959712f8acSEd Tanous UnpackTypes&... in) 1969712f8acSEd Tanous { 1979712f8acSEd Tanous if (key != keyToCheck) 1989712f8acSEd Tanous { 1999712f8acSEd Tanous readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 2009712f8acSEd Tanous return; 2019712f8acSEd Tanous } 2029712f8acSEd Tanous 2039712f8acSEd Tanous handled.set(Index); 2049712f8acSEd Tanous 205771cfa0fSJason M. Bills unpackValue<ValueType>(jsonValue, key, res, valueToFill); 2069712f8acSEd Tanous } 2079712f8acSEd Tanous 2089712f8acSEd Tanous template <size_t Index = 0, size_t Count> 2099712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res) 2109712f8acSEd Tanous { 2119712f8acSEd Tanous } 2129712f8acSEd Tanous 2139712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType, 2149712f8acSEd Tanous typename... UnpackTypes> 2159712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res, 2169712f8acSEd Tanous const char* key, ValueType& unused, UnpackTypes&... in) 2179712f8acSEd Tanous { 218771cfa0fSJason M. Bills if (!handled.test(Index) && !is_optional_v<ValueType>) 2199712f8acSEd Tanous { 220a08b46ccSJason M. Bills messages::propertyMissing(res, key); 2219712f8acSEd Tanous } 2229712f8acSEd Tanous details::handleMissing<Index + 1, Count>(handled, res, in...); 2239712f8acSEd Tanous } 2249712f8acSEd Tanous } // namespace details 2259712f8acSEd Tanous 2269712f8acSEd Tanous template <typename... UnpackTypes> 2270627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 2289712f8acSEd Tanous UnpackTypes&... in) 2299712f8acSEd Tanous { 2309712f8acSEd Tanous if (!jsonRequest.is_object()) 2319712f8acSEd Tanous { 2329712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 233f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 2349712f8acSEd Tanous return false; 2359712f8acSEd Tanous } 2369712f8acSEd Tanous 2379712f8acSEd Tanous if (jsonRequest.empty()) 2389712f8acSEd Tanous { 2399712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is empty"; 240f12894f8SJason M. Bills messages::emptyJSON(res); 2419712f8acSEd Tanous return false; 2429712f8acSEd Tanous } 2439712f8acSEd Tanous 2449712f8acSEd Tanous std::bitset<(sizeof...(in) + 1) / 2> handled(0); 2459712f8acSEd Tanous for (const auto& item : jsonRequest.items()) 2469712f8acSEd Tanous { 2479712f8acSEd Tanous details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 2489712f8acSEd Tanous item.key(), item.value(), res, handled, key, in...); 2499712f8acSEd Tanous } 2509712f8acSEd Tanous 2519712f8acSEd Tanous details::handleMissing(handled, res, key, in...); 2529712f8acSEd Tanous 2539712f8acSEd Tanous return res.result() == boost::beast::http::status::ok; 2549712f8acSEd Tanous } 25577dd8813SKowalski, Kamil 2560627a2c7SEd Tanous template <typename... UnpackTypes> 2570627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key, 2580627a2c7SEd Tanous UnpackTypes&... in) 2590627a2c7SEd Tanous { 2600627a2c7SEd Tanous nlohmann::json jsonRequest; 2610627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 2620627a2c7SEd Tanous { 2630627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 2640627a2c7SEd Tanous return false; 2650627a2c7SEd Tanous } 2660627a2c7SEd Tanous return readJson(jsonRequest, res, key, in...); 2670627a2c7SEd Tanous } 2680627a2c7SEd Tanous 26977dd8813SKowalski, Kamil } // namespace json_util 27077dd8813SKowalski, Kamil } // namespace redfish 271