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