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