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 template <typename Type> struct unpackValue 46 { 47 using isRequired = std::true_type; 48 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 49 }; 50 51 template <typename OptionalType> 52 struct unpackValue<boost::optional<OptionalType>> 53 { 54 using isRequired = std::false_type; 55 using JsonType = std::add_const_t<std::add_pointer_t<OptionalType>>; 56 }; 57 58 template <size_t Count, size_t Index> 59 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 60 crow::Response& res, std::bitset<Count>& handled) 61 { 62 BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 63 messages::propertyUnknown(res, key, key); 64 } 65 66 template <size_t Count, size_t Index, typename ValueType, 67 typename... UnpackTypes> 68 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 69 crow::Response& res, std::bitset<Count>& handled, 70 const char* keyToCheck, ValueType& valueToFill, 71 UnpackTypes&... in) 72 { 73 if (key != keyToCheck) 74 { 75 readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 76 return; 77 } 78 79 handled.set(Index); 80 81 using UnpackType = typename unpackValue<ValueType>::JsonType; 82 UnpackType value = jsonValue.get_ptr<UnpackType>(); 83 if (value == nullptr) 84 { 85 BMCWEB_LOG_DEBUG << "Value for key " << key 86 << " was incorrect type: " << jsonValue.type_name(); 87 messages::propertyValueTypeError(res, jsonValue.dump(), key, key); 88 return; 89 } 90 91 valueToFill = *value; 92 } 93 94 template <size_t Index = 0, size_t Count> 95 void handleMissing(std::bitset<Count>& handled, crow::Response& res) 96 { 97 } 98 99 template <size_t Index = 0, size_t Count, typename ValueType, 100 typename... UnpackTypes> 101 void handleMissing(std::bitset<Count>& handled, crow::Response& res, 102 const char* key, ValueType& unused, UnpackTypes&... in) 103 { 104 if (!handled.test(Index) && unpackValue<ValueType>::isRequired::value) 105 { 106 messages::propertyMissing(res, key, key); 107 } 108 details::handleMissing<Index + 1, Count>(handled, res, in...); 109 } 110 } // namespace details 111 112 template <typename... UnpackTypes> 113 bool readJson(const crow::Request& req, crow::Response& res, const char* key, 114 UnpackTypes&... in) 115 { 116 nlohmann::json jsonRequest; 117 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 118 { 119 BMCWEB_LOG_DEBUG << "Json value not readable"; 120 return false; 121 } 122 if (!jsonRequest.is_object()) 123 { 124 BMCWEB_LOG_DEBUG << "Json value is not an object"; 125 messages::unrecognizedRequestBody(res); 126 return false; 127 } 128 129 if (jsonRequest.empty()) 130 { 131 BMCWEB_LOG_DEBUG << "Json value is empty"; 132 messages::emptyJSON(res); 133 return false; 134 } 135 136 std::bitset<(sizeof...(in) + 1) / 2> handled(0); 137 for (const auto& item : jsonRequest.items()) 138 { 139 details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 140 item.key(), item.value(), res, handled, key, in...); 141 } 142 143 if (!handled.all()) 144 { 145 details::handleMissing(handled, res, key, in...); 146 147 return false; 148 } 149 return res.result() == boost::beast::http::status::ok; 150 } 151 152 } // namespace json_util 153 } // namespace redfish 154