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