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