1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include <crow/http_request.h> 19 #include <crow/http_response.h> 20 21 #include <bitset> 22 #include <error_messages.hpp> 23 #include <nlohmann/json.hpp> 24 namespace redfish 25 { 26 27 namespace json_util 28 { 29 30 /** 31 * @brief Processes request to extract JSON from its body. If it fails, adds 32 * MalformedJSON message to response and ends it. 33 * 34 * @param[io] res Response object 35 * @param[in] req Request object 36 * @param[out] reqJson JSON object extracted from request's body 37 * 38 * @return true if JSON is valid, false when JSON is invalid and response has 39 * been filled with message and ended. 40 */ 41 bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 42 nlohmann::json& reqJson); 43 namespace details 44 { 45 46 template <typename Type> struct is_optional : std::false_type 47 { 48 }; 49 50 template <typename Type> 51 struct is_optional<std::optional<Type>> : std::true_type 52 { 53 }; 54 55 template <typename Type> 56 constexpr bool is_optional_v = is_optional<Type>::value; 57 58 template <typename Type> struct is_vector : std::false_type 59 { 60 }; 61 62 template <typename Type> struct is_vector<std::vector<Type>> : std::true_type 63 { 64 }; 65 66 template <typename Type> constexpr bool is_vector_v = is_vector<Type>::value; 67 68 template <typename Type> 69 void unpackValue(nlohmann::json& jsonValue, const std::string& key, 70 crow::Response& res, Type& value) 71 { 72 if constexpr (std::is_arithmetic_v<Type>) 73 { 74 using NumType = 75 std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 76 77 NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 78 if (jsonPtr == nullptr) 79 { 80 BMCWEB_LOG_DEBUG 81 << "Value for key " << key 82 << " was incorrect type: " << jsonValue.type_name(); 83 messages::propertyValueTypeError(res, jsonValue.dump(), key); 84 return; 85 } 86 if (*jsonPtr > std::numeric_limits<Type>::max()) 87 { 88 BMCWEB_LOG_DEBUG << "Value for key " << key 89 << " was out of range: " << jsonValue.type_name(); 90 messages::propertyValueNotInList(res, jsonValue.dump(), key); 91 return; 92 } 93 if (*jsonPtr < std::numeric_limits<Type>::min()) 94 { 95 BMCWEB_LOG_DEBUG << "Value for key " << key 96 << " was out of range: " << jsonValue.type_name(); 97 messages::propertyValueNotInList(res, jsonValue.dump(), key); 98 return; 99 } 100 value = static_cast<Type>(*jsonPtr); 101 } 102 else if constexpr (is_optional_v<Type>) 103 { 104 value.emplace(); 105 unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 106 } 107 else if constexpr (is_vector_v<Type>) 108 { 109 if (!jsonValue.is_array()) 110 { 111 messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 112 return; 113 } 114 115 for (const auto& val : jsonValue.items()) 116 { 117 value.emplace_back(); 118 unpackValue<typename Type::value_type>(val.value(), key, res, 119 value.back()); 120 } 121 } 122 else 123 { 124 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 125 JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 126 if (jsonPtr == nullptr) 127 { 128 BMCWEB_LOG_DEBUG 129 << "Value for key " << key 130 << " was incorrect type: " << jsonValue.type_name(); 131 messages::propertyValueTypeError(res, jsonValue.dump(), key); 132 return; 133 } 134 value = std::move(*jsonPtr); 135 } 136 } 137 138 template <size_t Count, size_t Index> 139 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 140 crow::Response& res, std::bitset<Count>& handled) 141 { 142 BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 143 messages::propertyUnknown(res, key); 144 } 145 146 template <size_t Count, size_t Index, typename ValueType, 147 typename... UnpackTypes> 148 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 149 crow::Response& res, std::bitset<Count>& handled, 150 const char* keyToCheck, ValueType& valueToFill, 151 UnpackTypes&... in) 152 { 153 if (key != keyToCheck) 154 { 155 readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 156 return; 157 } 158 159 handled.set(Index); 160 161 unpackValue<ValueType>(jsonValue, key, res, valueToFill); 162 } 163 164 template <size_t Index = 0, size_t Count> 165 void handleMissing(std::bitset<Count>& handled, crow::Response& res) 166 { 167 } 168 169 template <size_t Index = 0, size_t Count, typename ValueType, 170 typename... UnpackTypes> 171 void handleMissing(std::bitset<Count>& handled, crow::Response& res, 172 const char* key, ValueType& unused, UnpackTypes&... in) 173 { 174 if (!handled.test(Index) && !is_optional_v<ValueType>) 175 { 176 messages::propertyMissing(res, key); 177 } 178 details::handleMissing<Index + 1, Count>(handled, res, in...); 179 } 180 } // namespace details 181 182 template <typename... UnpackTypes> 183 bool readJson(const crow::Request& req, crow::Response& res, const char* key, 184 UnpackTypes&... in) 185 { 186 nlohmann::json jsonRequest; 187 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 188 { 189 BMCWEB_LOG_DEBUG << "Json value not readable"; 190 return false; 191 } 192 if (!jsonRequest.is_object()) 193 { 194 BMCWEB_LOG_DEBUG << "Json value is not an object"; 195 messages::unrecognizedRequestBody(res); 196 return false; 197 } 198 199 if (jsonRequest.empty()) 200 { 201 BMCWEB_LOG_DEBUG << "Json value is empty"; 202 messages::emptyJSON(res); 203 return false; 204 } 205 206 std::bitset<(sizeof...(in) + 1) / 2> handled(0); 207 for (const auto& item : jsonRequest.items()) 208 { 209 details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 210 item.key(), item.value(), res, handled, key, in...); 211 } 212 213 details::handleMissing(handled, res, key, in...); 214 215 return res.result() == boost::beast::http::status::ok; 216 } 217 218 } // namespace json_util 219 } // namespace redfish 220