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 <http_request.h> 19 #include <http_response.h> 20 21 #include <bitset> 22 #include <error_messages.hpp> 23 #include <nlohmann/json.hpp> 24 25 namespace redfish 26 { 27 28 namespace json_util 29 { 30 31 /** 32 * @brief Processes request to extract JSON from its body. If it fails, adds 33 * MalformedJSON message to response and ends it. 34 * 35 * @param[io] res Response object 36 * @param[in] req Request object 37 * @param[out] reqJson JSON object extracted from request's body 38 * 39 * @return true if JSON is valid, false when JSON is invalid and response has 40 * been filled with message and ended. 41 */ 42 bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 43 nlohmann::json& reqJson); 44 namespace details 45 { 46 47 template <typename Type> struct is_optional : std::false_type 48 { 49 }; 50 51 template <typename Type> 52 struct is_optional<std::optional<Type>> : std::true_type 53 { 54 }; 55 56 template <typename Type> 57 constexpr bool is_optional_v = is_optional<Type>::value; 58 59 template <typename Type> struct is_vector : std::false_type 60 { 61 }; 62 63 template <typename Type> struct is_vector<std::vector<Type>> : std::true_type 64 { 65 }; 66 67 template <typename Type> constexpr bool is_vector_v = is_vector<Type>::value; 68 69 template <typename Type> struct is_std_array : std::false_type 70 { 71 }; 72 73 template <typename Type, std::size_t size> 74 struct is_std_array<std::array<Type, size>> : std::true_type 75 { 76 }; 77 78 template <typename Type> 79 constexpr bool is_std_array_v = is_std_array<Type>::value; 80 81 template <typename ToType, typename FromType> 82 bool checkRange(const FromType& from, const std::string& key, 83 nlohmann::json& jsonValue, crow::Response& res) 84 { 85 if (from > std::numeric_limits<ToType>::max()) 86 { 87 BMCWEB_LOG_DEBUG << "Value for key " << key 88 << " was greater than max: " << __PRETTY_FUNCTION__; 89 messages::propertyValueNotInList(res, jsonValue.dump(), key); 90 return false; 91 } 92 if (from < std::numeric_limits<ToType>::lowest()) 93 { 94 BMCWEB_LOG_DEBUG << "Value for key " << key 95 << " was less than min: " << __PRETTY_FUNCTION__; 96 messages::propertyValueNotInList(res, jsonValue.dump(), key); 97 return false; 98 } 99 if constexpr (std::is_floating_point_v<ToType>) 100 { 101 if (std::isnan(from)) 102 { 103 BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN"; 104 messages::propertyValueNotInList(res, jsonValue.dump(), key); 105 return false; 106 } 107 } 108 109 return true; 110 } 111 112 template <typename Type> 113 bool unpackValue(nlohmann::json& jsonValue, const std::string& key, 114 crow::Response& res, Type& value) 115 { 116 bool ret = true; 117 118 if constexpr (std::is_floating_point_v<Type>) 119 { 120 double helper = 0; 121 double* jsonPtr = jsonValue.get_ptr<double*>(); 122 123 if (jsonPtr == nullptr) 124 { 125 int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 126 if (intPtr != nullptr) 127 { 128 helper = static_cast<double>(*intPtr); 129 jsonPtr = &helper; 130 } 131 } 132 if (jsonPtr == nullptr) 133 { 134 messages::propertyValueTypeError(res, jsonValue.dump(), key); 135 return false; 136 } 137 if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 138 { 139 return false; 140 } 141 value = static_cast<Type>(*jsonPtr); 142 } 143 144 else if constexpr (std::is_signed_v<Type>) 145 { 146 int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 147 if (jsonPtr == nullptr) 148 { 149 messages::propertyValueTypeError(res, jsonValue.dump(), key); 150 return false; 151 } 152 if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 153 { 154 return false; 155 } 156 value = static_cast<Type>(*jsonPtr); 157 } 158 159 else if constexpr ((std::is_unsigned_v<Type>)&&( 160 !std::is_same_v<bool, Type>)) 161 { 162 uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 163 if (jsonPtr == nullptr) 164 { 165 messages::propertyValueTypeError(res, jsonValue.dump(), key); 166 return false; 167 } 168 if (!checkRange<Type>(*jsonPtr, key, jsonValue, res)) 169 { 170 return false; 171 } 172 value = static_cast<Type>(*jsonPtr); 173 } 174 175 else if constexpr (is_optional_v<Type>) 176 { 177 value.emplace(); 178 ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 179 *value) && 180 ret; 181 } 182 else if constexpr (std::is_same_v<nlohmann::json, Type>) 183 { 184 // Must be a complex type. Simple types (int string etc) should be 185 // unpacked directly 186 if (!jsonValue.is_object() && !jsonValue.is_array() && 187 !jsonValue.is_null()) 188 { 189 messages::propertyValueTypeError(res, jsonValue.dump(), key); 190 return false; 191 } 192 193 value = std::move(jsonValue); 194 } 195 else if constexpr (is_std_array_v<Type>) 196 { 197 if (!jsonValue.is_array()) 198 { 199 messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 200 return false; 201 } 202 if (jsonValue.size() != value.size()) 203 { 204 messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 205 return false; 206 } 207 size_t index = 0; 208 for (const auto& val : jsonValue.items()) 209 { 210 ret = unpackValue<typename Type::value_type>(val.value(), key, res, 211 value[index++]) && 212 ret; 213 } 214 } 215 else if constexpr (is_vector_v<Type>) 216 { 217 if (!jsonValue.is_array()) 218 { 219 messages::propertyValueTypeError(res, res.jsonValue.dump(), key); 220 return false; 221 } 222 223 for (const auto& val : jsonValue.items()) 224 { 225 value.emplace_back(); 226 ret = unpackValue<typename Type::value_type>(val.value(), key, res, 227 value.back()) && 228 ret; 229 } 230 } 231 else 232 { 233 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 234 JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 235 if (jsonPtr == nullptr) 236 { 237 BMCWEB_LOG_DEBUG 238 << "Value for key " << key 239 << " was incorrect type: " << jsonValue.type_name(); 240 messages::propertyValueTypeError(res, jsonValue.dump(), key); 241 return false; 242 } 243 value = std::move(*jsonPtr); 244 } 245 return ret; 246 } 247 248 template <size_t Count, size_t Index> 249 bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 250 crow::Response& res, std::bitset<Count>& handled) 251 { 252 BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 253 messages::propertyUnknown(res, key); 254 return false; 255 } 256 257 template <size_t Count, size_t Index, typename ValueType, 258 typename... UnpackTypes> 259 bool readJsonValues(const std::string& key, nlohmann::json& jsonValue, 260 crow::Response& res, std::bitset<Count>& handled, 261 const char* keyToCheck, ValueType& valueToFill, 262 UnpackTypes&... in) 263 { 264 bool ret = true; 265 if (key != keyToCheck) 266 { 267 ret = readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, 268 in...) && 269 ret; 270 return ret; 271 } 272 273 handled.set(Index); 274 275 return unpackValue<ValueType>(jsonValue, key, res, valueToFill) && ret; 276 } 277 278 template <size_t Index = 0, size_t Count> 279 bool handleMissing(std::bitset<Count>& handled, crow::Response& res) 280 { 281 return true; 282 } 283 284 template <size_t Index = 0, size_t Count, typename ValueType, 285 typename... UnpackTypes> 286 bool handleMissing(std::bitset<Count>& handled, crow::Response& res, 287 const char* key, ValueType& unused, UnpackTypes&... in) 288 { 289 bool ret = true; 290 if (!handled.test(Index) && !is_optional_v<ValueType>) 291 { 292 ret = false; 293 messages::propertyMissing(res, key); 294 } 295 return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret; 296 } 297 } // namespace details 298 299 template <typename... UnpackTypes> 300 bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key, 301 UnpackTypes&... in) 302 { 303 bool result = true; 304 if (!jsonRequest.is_object()) 305 { 306 BMCWEB_LOG_DEBUG << "Json value is not an object"; 307 messages::unrecognizedRequestBody(res); 308 return false; 309 } 310 311 if (jsonRequest.empty()) 312 { 313 BMCWEB_LOG_DEBUG << "Json value is empty"; 314 messages::emptyJSON(res); 315 return false; 316 } 317 318 std::bitset<(sizeof...(in) + 1) / 2> handled(0); 319 for (const auto& item : jsonRequest.items()) 320 { 321 result = 322 details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 323 item.key(), item.value(), res, handled, key, in...) && 324 result; 325 } 326 327 BMCWEB_LOG_DEBUG << "JSON result is: " << result; 328 329 return details::handleMissing(handled, res, key, in...) && result; 330 } 331 332 template <typename... UnpackTypes> 333 bool readJson(const crow::Request& req, crow::Response& res, const char* key, 334 UnpackTypes&... in) 335 { 336 nlohmann::json jsonRequest; 337 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 338 { 339 BMCWEB_LOG_DEBUG << "Json value not readable"; 340 return false; 341 } 342 return readJson(jsonRequest, res, key, in...); 343 } 344 345 } // namespace json_util 346 } // namespace redfish 347