xref: /openbmc/bmcweb/include/event_service_store.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 #include "logging.hpp"
5 
6 #include <boost/beast/http/fields.hpp>
7 #include <boost/container/flat_map.hpp>
8 #include <boost/url/parse.hpp>
9 #include <boost/url/url.hpp>
10 #include <nlohmann/json.hpp>
11 
12 #include <limits>
13 #include <memory>
14 #include <string>
15 #include <vector>
16 
17 namespace persistent_data
18 {
19 
20 struct UserSubscription
21 {
22     // Represents a Redfish EventDestination instance
23     std::string id;
24     boost::urls::url destinationUrl;
25     std::string protocol;
26     bool verifyCertificate = true;
27     std::string retryPolicy;
28     bool sendHeartbeat = false;
29     // This value of hbIntervalMinutes is just a reasonable default value and
30     // most clients will update it if sendHeartbeat is turned on
31     uint64_t hbIntervalMinutes = 10;
32     std::string customText;
33     std::string eventFormatType;
34     std::string subscriptionType;
35     std::vector<std::string> registryMsgIds;
36     std::vector<std::string> registryPrefixes;
37     std::vector<std::string> resourceTypes;
38     boost::beast::http::fields httpHeaders;
39     std::vector<std::string> metricReportDefinitions;
40     std::vector<std::string> originResources;
41 
42     static std::optional<UserSubscription> fromJson(
43         const nlohmann::json::object_t& j, const bool loadFromOldConfig = false)
44     {
45         UserSubscription subvalue;
46         for (const auto& element : j)
47         {
48             if (element.first == "Id")
49             {
50                 const std::string* value =
51                     element.second.get_ptr<const std::string*>();
52                 if (value == nullptr)
53                 {
54                     continue;
55                 }
56                 subvalue.id = *value;
57             }
58             else if (element.first == "Destination")
59             {
60                 const std::string* value =
61                     element.second.get_ptr<const std::string*>();
62                 if (value == nullptr)
63                 {
64                     continue;
65                 }
66                 boost::system::result<boost::urls::url> url =
67                     boost::urls::parse_absolute_uri(*value);
68                 if (!url)
69                 {
70                     continue;
71                 }
72                 subvalue.destinationUrl = std::move(*url);
73             }
74             else if (element.first == "Protocol")
75             {
76                 const std::string* value =
77                     element.second.get_ptr<const std::string*>();
78                 if (value == nullptr)
79                 {
80                     continue;
81                 }
82                 subvalue.protocol = *value;
83             }
84             else if (element.first == "VerifyCertificate")
85             {
86                 const bool* value = element.second.get_ptr<const bool*>();
87                 if (value == nullptr)
88                 {
89                     continue;
90                 }
91                 subvalue.verifyCertificate = *value;
92             }
93             else if (element.first == "DeliveryRetryPolicy")
94             {
95                 const std::string* value =
96                     element.second.get_ptr<const std::string*>();
97                 if (value == nullptr)
98                 {
99                     continue;
100                 }
101                 subvalue.retryPolicy = *value;
102             }
103             else if (element.first == "SendHeartbeat")
104             {
105                 const bool* value = element.second.get_ptr<const bool*>();
106                 if (value == nullptr)
107                 {
108                     continue;
109                 }
110                 subvalue.sendHeartbeat = *value;
111             }
112             else if (element.first == "HeartbeatIntervalMinutes")
113             {
114                 const uint64_t* value =
115                     element.second.get_ptr<const uint64_t*>();
116                 if (value == nullptr || *value < 1 || *value > 65535)
117                 {
118                     continue;
119                 }
120                 subvalue.hbIntervalMinutes = *value;
121             }
122             else if (element.first == "Context")
123             {
124                 const std::string* value =
125                     element.second.get_ptr<const std::string*>();
126                 if (value == nullptr)
127                 {
128                     continue;
129                 }
130                 subvalue.customText = *value;
131             }
132             else if (element.first == "EventFormatType")
133             {
134                 const std::string* value =
135                     element.second.get_ptr<const std::string*>();
136                 if (value == nullptr)
137                 {
138                     continue;
139                 }
140                 subvalue.eventFormatType = *value;
141             }
142             else if (element.first == "SubscriptionType")
143             {
144                 const std::string* value =
145                     element.second.get_ptr<const std::string*>();
146                 if (value == nullptr)
147                 {
148                     continue;
149                 }
150                 subvalue.subscriptionType = *value;
151             }
152             else if (element.first == "MessageIds")
153             {
154                 const nlohmann::json::array_t* obj =
155                     element.second.get_ptr<const nlohmann::json::array_t*>();
156                 if (obj == nullptr)
157                 {
158                     continue;
159                 }
160                 for (const auto& val : *obj)
161                 {
162                     const std::string* value =
163                         val.get_ptr<const std::string*>();
164                     if (value == nullptr)
165                     {
166                         continue;
167                     }
168                     subvalue.registryMsgIds.emplace_back(*value);
169                 }
170             }
171             else if (element.first == "RegistryPrefixes")
172             {
173                 const nlohmann::json::array_t* obj =
174                     element.second.get_ptr<const nlohmann::json::array_t*>();
175                 if (obj == nullptr)
176                 {
177                     continue;
178                 }
179                 for (const auto& val : *obj)
180                 {
181                     const std::string* value =
182                         val.get_ptr<const std::string*>();
183                     if (value == nullptr)
184                     {
185                         continue;
186                     }
187                     subvalue.registryPrefixes.emplace_back(*value);
188                 }
189             }
190             else if (element.first == "ResourceTypes")
191             {
192                 const nlohmann::json::array_t* obj =
193                     element.second.get_ptr<const nlohmann::json::array_t*>();
194                 if (obj == nullptr)
195                 {
196                     continue;
197                 }
198                 for (const auto& val : *obj)
199                 {
200                     const std::string* value =
201                         val.get_ptr<const std::string*>();
202                     if (value == nullptr)
203                     {
204                         continue;
205                     }
206                     subvalue.resourceTypes.emplace_back(*value);
207                 }
208             }
209             else if (element.first == "HttpHeaders")
210             {
211                 const nlohmann::json::object_t* obj =
212                     element.second.get_ptr<const nlohmann::json::object_t*>();
213                 if (obj == nullptr)
214                 {
215                     continue;
216                 }
217                 for (const auto& val : *obj)
218                 {
219                     const std::string* value =
220                         val.second.get_ptr<const std::string*>();
221                     if (value == nullptr)
222                     {
223                         BMCWEB_LOG_ERROR("Failed to parse value for key{}",
224                                          val.first);
225                         continue;
226                     }
227                     subvalue.httpHeaders.set(val.first, *value);
228                 }
229             }
230             else if (element.first == "MetricReportDefinitions")
231             {
232                 const nlohmann::json::array_t* obj =
233                     element.second.get_ptr<const nlohmann::json::array_t*>();
234                 if (obj == nullptr)
235                 {
236                     continue;
237                 }
238                 for (const auto& val : *obj)
239                 {
240                     const std::string* value =
241                         val.get_ptr<const std::string*>();
242                     if (value == nullptr)
243                     {
244                         continue;
245                     }
246                     subvalue.metricReportDefinitions.emplace_back(*value);
247                 }
248             }
249             else if (element.first == "OriginResources")
250             {
251                 const nlohmann::json::array_t* obj =
252                     element.second.get_ptr<const nlohmann::json::array_t*>();
253                 if (obj == nullptr)
254                 {
255                     continue;
256                 }
257                 for (const auto& val : *obj)
258                 {
259                     const std::string* value =
260                         val.get_ptr<const std::string*>();
261                     if (value == nullptr)
262                     {
263                         continue;
264                     }
265                     subvalue.originResources.emplace_back(*value);
266                 }
267             }
268             else
269             {
270                 BMCWEB_LOG_ERROR(
271                     "Got unexpected property reading persistent file: {}",
272                     element.first);
273                 continue;
274             }
275         }
276 
277         if ((subvalue.id.empty() && !loadFromOldConfig) ||
278             subvalue.destinationUrl.empty() || subvalue.protocol.empty() ||
279             subvalue.retryPolicy.empty() || subvalue.eventFormatType.empty() ||
280             subvalue.subscriptionType.empty())
281         {
282             BMCWEB_LOG_ERROR("Subscription missing required field "
283                              "information, refusing to restore");
284             return std::nullopt;
285         }
286 
287         return subvalue;
288     }
289 };
290 
291 struct EventServiceConfig
292 {
293     bool enabled = true;
294     uint32_t retryAttempts = 3;
295     uint32_t retryTimeoutInterval = 30;
296 
297     void fromJson(const nlohmann::json::object_t& j)
298     {
299         for (const auto& element : j)
300         {
301             if (element.first == "ServiceEnabled")
302             {
303                 const bool* value = element.second.get_ptr<const bool*>();
304                 if (value == nullptr)
305                 {
306                     continue;
307                 }
308                 enabled = *value;
309             }
310             else if (element.first == "DeliveryRetryAttempts")
311             {
312                 const uint64_t* value =
313                     element.second.get_ptr<const uint64_t*>();
314                 if ((value == nullptr) ||
315                     (*value > std::numeric_limits<uint32_t>::max()))
316                 {
317                     continue;
318                 }
319                 retryAttempts = static_cast<uint32_t>(*value);
320             }
321             else if (element.first == "DeliveryRetryIntervalSeconds")
322             {
323                 const uint64_t* value =
324                     element.second.get_ptr<const uint64_t*>();
325                 if ((value == nullptr) ||
326                     (*value > std::numeric_limits<uint32_t>::max()))
327                 {
328                     continue;
329                 }
330                 retryTimeoutInterval = static_cast<uint32_t>(*value);
331             }
332         }
333     }
334 };
335 
336 class EventServiceStore
337 {
338   public:
339     boost::container::flat_map<std::string, std::shared_ptr<UserSubscription>>
340         subscriptionsConfigMap;
341     EventServiceConfig eventServiceConfig;
342 
343     static EventServiceStore& getInstance()
344     {
345         static EventServiceStore eventServiceStore;
346         return eventServiceStore;
347     }
348 
349     EventServiceConfig& getEventServiceConfig()
350     {
351         return eventServiceConfig;
352     }
353 };
354 
355 } // namespace persistent_data
356