xref: /openbmc/bmcweb/redfish-core/include/utils/json_utils.hpp (revision aee8d847dc4d46030aa016818e5d0dc9d62feae4)
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