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