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<boost::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> 59 void unpackValue(nlohmann::json& jsonValue, const std::string& key, 60 crow::Response& res, Type& value) 61 { 62 if constexpr (std::is_arithmetic_v<Type>) 63 { 64 using NumType = 65 std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>; 66 67 NumType* jsonPtr = jsonValue.get_ptr<NumType*>(); 68 if (jsonPtr == nullptr) 69 { 70 BMCWEB_LOG_DEBUG 71 << "Value for key " << key 72 << " was incorrect type: " << jsonValue.type_name(); 73 messages::propertyValueTypeError(res, jsonValue.dump(), key); 74 return; 75 } 76 if (*jsonPtr > std::numeric_limits<Type>::max()) 77 { 78 BMCWEB_LOG_DEBUG << "Value for key " << key 79 << " was out of range: " << jsonValue.type_name(); 80 messages::propertyValueNotInList(res, jsonValue.dump(), key); 81 return; 82 } 83 if (*jsonPtr < std::numeric_limits<Type>::min()) 84 { 85 BMCWEB_LOG_DEBUG << "Value for key " << key 86 << " was out of range: " << jsonValue.type_name(); 87 messages::propertyValueNotInList(res, jsonValue.dump(), key); 88 return; 89 } 90 value = static_cast<Type>(*jsonPtr); 91 } 92 else if constexpr (is_optional_v<Type>) 93 { 94 value.emplace(); 95 unpackValue<typename Type::value_type>(jsonValue, key, res, *value); 96 } 97 else 98 { 99 using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 100 JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 101 if (jsonPtr == nullptr) 102 { 103 BMCWEB_LOG_DEBUG 104 << "Value for key " << key 105 << " was incorrect type: " << jsonValue.type_name(); 106 messages::propertyValueTypeError(res, jsonValue.dump(), key); 107 return; 108 } 109 value = std::move(*jsonPtr); 110 } 111 } 112 113 template <size_t Count, size_t Index> 114 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 115 crow::Response& res, std::bitset<Count>& handled) 116 { 117 BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key; 118 messages::propertyUnknown(res, key); 119 } 120 121 template <size_t Count, size_t Index, typename ValueType, 122 typename... UnpackTypes> 123 void readJsonValues(const std::string& key, nlohmann::json& jsonValue, 124 crow::Response& res, std::bitset<Count>& handled, 125 const char* keyToCheck, ValueType& valueToFill, 126 UnpackTypes&... in) 127 { 128 if (key != keyToCheck) 129 { 130 readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...); 131 return; 132 } 133 134 handled.set(Index); 135 136 unpackValue<ValueType>(jsonValue, key, res, valueToFill); 137 } 138 139 template <size_t Index = 0, size_t Count> 140 void handleMissing(std::bitset<Count>& handled, crow::Response& res) 141 { 142 } 143 144 template <size_t Index = 0, size_t Count, typename ValueType, 145 typename... UnpackTypes> 146 void handleMissing(std::bitset<Count>& handled, crow::Response& res, 147 const char* key, ValueType& unused, UnpackTypes&... in) 148 { 149 if (!handled.test(Index) && !is_optional_v<ValueType>) 150 { 151 messages::propertyMissing(res, key); 152 } 153 details::handleMissing<Index + 1, Count>(handled, res, in...); 154 } 155 } // namespace details 156 157 template <typename... UnpackTypes> 158 bool readJson(const crow::Request& req, crow::Response& res, const char* key, 159 UnpackTypes&... in) 160 { 161 nlohmann::json jsonRequest; 162 if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 163 { 164 BMCWEB_LOG_DEBUG << "Json value not readable"; 165 return false; 166 } 167 if (!jsonRequest.is_object()) 168 { 169 BMCWEB_LOG_DEBUG << "Json value is not an object"; 170 messages::unrecognizedRequestBody(res); 171 return false; 172 } 173 174 if (jsonRequest.empty()) 175 { 176 BMCWEB_LOG_DEBUG << "Json value is empty"; 177 messages::emptyJSON(res); 178 return false; 179 } 180 181 std::bitset<(sizeof...(in) + 1) / 2> handled(0); 182 for (const auto& item : jsonRequest.items()) 183 { 184 details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>( 185 item.key(), item.value(), res, handled, key, in...); 186 } 187 188 details::handleMissing(handled, res, key, in...); 189 190 return res.result() == boost::beast::http::status::ok; 191 } 192 193 } // namespace json_util 194 } // namespace redfish 195