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