1 /* 2 // Copyright (c) 2020 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 #include "app.hpp" 18 #include "event_service_manager.hpp" 19 #include "http/utility.hpp" 20 #include "logging.hpp" 21 #include "query.hpp" 22 #include "registries/privilege_registry.hpp" 23 #include "snmp_trap_event_clients.hpp" 24 25 #include <boost/beast/http/fields.hpp> 26 #include <boost/system/error_code.hpp> 27 #include <sdbusplus/unpack_properties.hpp> 28 #include <utils/dbus_utils.hpp> 29 30 #include <charconv> 31 #include <memory> 32 #include <ranges> 33 #include <span> 34 #include <string> 35 36 namespace redfish 37 { 38 39 static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = { 40 eventFormatType, metricReportFormatType}; 41 static constexpr const std::array<const char*, 3> supportedRegPrefixes = { 42 "Base", "OpenBMC", "TaskEvent"}; 43 static constexpr const std::array<const char*, 3> supportedRetryPolicies = { 44 "TerminateAfterRetries", "SuspendRetries", "RetryForever"}; 45 46 static constexpr const std::array<const char*, 1> supportedResourceTypes = { 47 "Task"}; 48 49 inline void requestRoutesEventService(App& app) 50 { 51 BMCWEB_ROUTE(app, "/redfish/v1/EventService/") 52 .privileges(redfish::privileges::getEventService) 53 .methods(boost::beast::http::verb::get)( 54 [&app](const crow::Request& req, 55 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 56 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 57 { 58 return; 59 } 60 61 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService"; 62 asyncResp->res.jsonValue["@odata.type"] = 63 "#EventService.v1_5_0.EventService"; 64 asyncResp->res.jsonValue["Id"] = "EventService"; 65 asyncResp->res.jsonValue["Name"] = "Event Service"; 66 asyncResp->res.jsonValue["ServerSentEventUri"] = 67 "/redfish/v1/EventService/SSE"; 68 69 asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] = 70 "/redfish/v1/EventService/Subscriptions"; 71 asyncResp->res 72 .jsonValue["Actions"]["#EventService.SubmitTestEvent"]["target"] = 73 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"; 74 75 const persistent_data::EventServiceConfig eventServiceConfig = 76 persistent_data::EventServiceStore::getInstance() 77 .getEventServiceConfig(); 78 79 asyncResp->res.jsonValue["Status"]["State"] = 80 (eventServiceConfig.enabled ? "Enabled" : "Disabled"); 81 asyncResp->res.jsonValue["ServiceEnabled"] = eventServiceConfig.enabled; 82 asyncResp->res.jsonValue["DeliveryRetryAttempts"] = 83 eventServiceConfig.retryAttempts; 84 asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = 85 eventServiceConfig.retryTimeoutInterval; 86 asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes; 87 asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; 88 asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; 89 90 nlohmann::json::object_t supportedSSEFilters; 91 supportedSSEFilters["EventFormatType"] = true; 92 supportedSSEFilters["MessageId"] = true; 93 supportedSSEFilters["MetricReportDefinition"] = true; 94 supportedSSEFilters["RegistryPrefix"] = true; 95 supportedSSEFilters["OriginResource"] = false; 96 supportedSSEFilters["ResourceType"] = false; 97 98 asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = 99 std::move(supportedSSEFilters); 100 }); 101 102 BMCWEB_ROUTE(app, "/redfish/v1/EventService/") 103 .privileges(redfish::privileges::patchEventService) 104 .methods(boost::beast::http::verb::patch)( 105 [&app](const crow::Request& req, 106 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 107 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 108 { 109 return; 110 } 111 std::optional<bool> serviceEnabled; 112 std::optional<uint32_t> retryAttemps; 113 std::optional<uint32_t> retryInterval; 114 115 if (!json_util::readJsonPatch( 116 req, asyncResp->res, "ServiceEnabled", serviceEnabled, 117 "DeliveryRetryAttempts", retryAttemps, 118 "DeliveryRetryIntervalSeconds", retryInterval)) 119 { 120 return; 121 } 122 123 persistent_data::EventServiceConfig eventServiceConfig = 124 persistent_data::EventServiceStore::getInstance() 125 .getEventServiceConfig(); 126 127 if (serviceEnabled) 128 { 129 eventServiceConfig.enabled = *serviceEnabled; 130 } 131 132 if (retryAttemps) 133 { 134 // Supported range [1-3] 135 if ((*retryAttemps < 1) || (*retryAttemps > 3)) 136 { 137 messages::queryParameterOutOfRange( 138 asyncResp->res, std::to_string(*retryAttemps), 139 "DeliveryRetryAttempts", "[1-3]"); 140 } 141 else 142 { 143 eventServiceConfig.retryAttempts = *retryAttemps; 144 } 145 } 146 147 if (retryInterval) 148 { 149 // Supported range [5 - 180] 150 if ((*retryInterval < 5) || (*retryInterval > 180)) 151 { 152 messages::queryParameterOutOfRange( 153 asyncResp->res, std::to_string(*retryInterval), 154 "DeliveryRetryIntervalSeconds", "[5-180]"); 155 } 156 else 157 { 158 eventServiceConfig.retryTimeoutInterval = *retryInterval; 159 } 160 } 161 162 EventServiceManager::getInstance().setEventServiceConfig( 163 eventServiceConfig); 164 }); 165 } 166 167 inline void requestRoutesSubmitTestEvent(App& app) 168 { 169 BMCWEB_ROUTE( 170 app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/") 171 .privileges(redfish::privileges::postEventService) 172 .methods(boost::beast::http::verb::post)( 173 [&app](const crow::Request& req, 174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 175 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 176 { 177 return; 178 } 179 if (!EventServiceManager::getInstance().sendTestEventLog()) 180 { 181 messages::serviceDisabled(asyncResp->res, 182 "/redfish/v1/EventService/"); 183 return; 184 } 185 asyncResp->res.result(boost::beast::http::status::no_content); 186 }); 187 } 188 189 inline void doSubscriptionCollection( 190 const boost::system::error_code& ec, 191 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 192 const dbus::utility::ManagedObjectType& resp) 193 { 194 if (ec) 195 { 196 if (ec.value() == EBADR || ec.value() == EHOSTUNREACH) 197 { 198 // This is an optional process so just return if it isn't there 199 return; 200 } 201 202 BMCWEB_LOG_ERROR("D-Bus response error on GetManagedObjects {}", ec); 203 messages::internalError(asyncResp->res); 204 return; 205 } 206 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; 207 for (const auto& objpath : resp) 208 { 209 sdbusplus::message::object_path path(objpath.first); 210 const std::string snmpId = path.filename(); 211 if (snmpId.empty()) 212 { 213 BMCWEB_LOG_ERROR("The SNMP client ID is wrong"); 214 messages::internalError(asyncResp->res); 215 return; 216 } 217 218 getSnmpSubscriptionList(asyncResp, snmpId, memberArray); 219 } 220 } 221 222 inline void requestRoutesEventDestinationCollection(App& app) 223 { 224 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") 225 .privileges(redfish::privileges::getEventDestinationCollection) 226 .methods(boost::beast::http::verb::get)( 227 [&app](const crow::Request& req, 228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 229 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 230 { 231 return; 232 } 233 asyncResp->res.jsonValue["@odata.type"] = 234 "#EventDestinationCollection.EventDestinationCollection"; 235 asyncResp->res.jsonValue["@odata.id"] = 236 "/redfish/v1/EventService/Subscriptions"; 237 asyncResp->res.jsonValue["Name"] = "Event Destination Collections"; 238 239 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; 240 241 std::vector<std::string> subscripIds = 242 EventServiceManager::getInstance().getAllIDs(); 243 memberArray = nlohmann::json::array(); 244 asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size(); 245 246 for (const std::string& id : subscripIds) 247 { 248 nlohmann::json::object_t member; 249 member["@odata.id"] = boost::urls::format( 250 "/redfish/v1/EventService/Subscriptions/{}" + id); 251 memberArray.emplace_back(std::move(member)); 252 } 253 crow::connections::systemBus->async_method_call( 254 [asyncResp](const boost::system::error_code& ec, 255 const dbus::utility::ManagedObjectType& resp) { 256 doSubscriptionCollection(ec, asyncResp, resp); 257 }, 258 "xyz.openbmc_project.Network.SNMP", 259 "/xyz/openbmc_project/network/snmp/manager", 260 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 261 }); 262 263 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") 264 .privileges(redfish::privileges::postEventDestinationCollection) 265 .methods(boost::beast::http::verb::post)( 266 [&app](const crow::Request& req, 267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 268 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 269 { 270 return; 271 } 272 if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= 273 maxNoOfSubscriptions) 274 { 275 messages::eventSubscriptionLimitExceeded(asyncResp->res); 276 return; 277 } 278 std::string destUrl; 279 std::string protocol; 280 std::optional<std::string> context; 281 std::optional<std::string> subscriptionType; 282 std::optional<std::string> eventFormatType2; 283 std::optional<std::string> retryPolicy; 284 std::optional<std::vector<std::string>> msgIds; 285 std::optional<std::vector<std::string>> regPrefixes; 286 std::optional<std::vector<std::string>> resTypes; 287 std::optional<std::vector<nlohmann::json>> headers; 288 std::optional<std::vector<nlohmann::json>> mrdJsonArray; 289 290 if (!json_util::readJsonPatch( 291 req, asyncResp->res, "Destination", destUrl, "Context", context, 292 "Protocol", protocol, "SubscriptionType", subscriptionType, 293 "EventFormatType", eventFormatType2, "HttpHeaders", headers, 294 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, 295 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", 296 mrdJsonArray, "ResourceTypes", resTypes)) 297 { 298 return; 299 } 300 301 // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers 302 static constexpr const uint16_t maxDestinationSize = 2000; 303 if (destUrl.size() > maxDestinationSize) 304 { 305 messages::stringValueTooLong(asyncResp->res, "Destination", 306 maxDestinationSize); 307 return; 308 } 309 310 if (regPrefixes && msgIds) 311 { 312 if (!regPrefixes->empty() && !msgIds->empty()) 313 { 314 messages::propertyValueConflict(asyncResp->res, "MessageIds", 315 "RegistryPrefixes"); 316 return; 317 } 318 } 319 320 std::string host; 321 std::string urlProto; 322 uint16_t port = 0; 323 std::string path; 324 325 if (!crow::utility::validateAndSplitUrl(destUrl, urlProto, host, port, 326 path)) 327 { 328 BMCWEB_LOG_WARNING("Failed to validate and split destination url"); 329 messages::propertyValueFormatError(asyncResp->res, destUrl, 330 "Destination"); 331 return; 332 } 333 334 if (protocol == "SNMPv2c") 335 { 336 if (context) 337 { 338 messages::propertyValueConflict(asyncResp->res, "Context", 339 "Protocol"); 340 return; 341 } 342 if (eventFormatType2) 343 { 344 messages::propertyValueConflict(asyncResp->res, 345 "EventFormatType", "Protocol"); 346 return; 347 } 348 if (retryPolicy) 349 { 350 messages::propertyValueConflict(asyncResp->res, "RetryPolicy", 351 "Protocol"); 352 return; 353 } 354 if (msgIds) 355 { 356 messages::propertyValueConflict(asyncResp->res, "MessageIds", 357 "Protocol"); 358 return; 359 } 360 if (regPrefixes) 361 { 362 messages::propertyValueConflict(asyncResp->res, 363 "RegistryPrefixes", "Protocol"); 364 return; 365 } 366 if (resTypes) 367 { 368 messages::propertyValueConflict(asyncResp->res, "ResourceTypes", 369 "Protocol"); 370 return; 371 } 372 if (headers) 373 { 374 messages::propertyValueConflict(asyncResp->res, "HttpHeaders", 375 "Protocol"); 376 return; 377 } 378 if (mrdJsonArray) 379 { 380 messages::propertyValueConflict( 381 asyncResp->res, "MetricReportDefinitions", "Protocol"); 382 return; 383 } 384 385 addSnmpTrapClient(asyncResp, host, port); 386 return; 387 } 388 389 if (path.empty()) 390 { 391 path = "/"; 392 } 393 394 std::shared_ptr<Subscription> subValue = std::make_shared<Subscription>( 395 host, port, path, urlProto, app.ioContext()); 396 397 subValue->destinationUrl = destUrl; 398 399 if (subscriptionType) 400 { 401 if (*subscriptionType != "RedfishEvent") 402 { 403 messages::propertyValueNotInList( 404 asyncResp->res, *subscriptionType, "SubscriptionType"); 405 return; 406 } 407 subValue->subscriptionType = *subscriptionType; 408 } 409 else 410 { 411 subValue->subscriptionType = "RedfishEvent"; // Default 412 } 413 414 if (protocol != "Redfish") 415 { 416 messages::propertyValueNotInList(asyncResp->res, protocol, 417 "Protocol"); 418 return; 419 } 420 subValue->protocol = protocol; 421 422 if (eventFormatType2) 423 { 424 if (std::ranges::find(supportedEvtFormatTypes, *eventFormatType2) == 425 supportedEvtFormatTypes.end()) 426 { 427 messages::propertyValueNotInList( 428 asyncResp->res, *eventFormatType2, "EventFormatType"); 429 return; 430 } 431 subValue->eventFormatType = *eventFormatType2; 432 } 433 else 434 { 435 // If not specified, use default "Event" 436 subValue->eventFormatType = "Event"; 437 } 438 439 if (context) 440 { 441 // This value is selected aribitrarily. 442 constexpr const size_t maxContextSize = 256; 443 if (context->size() > maxContextSize) 444 { 445 messages::stringValueTooLong(asyncResp->res, "Context", 446 maxContextSize); 447 return; 448 } 449 subValue->customText = *context; 450 } 451 452 if (headers) 453 { 454 size_t cumulativeLen = 0; 455 456 for (const nlohmann::json& headerChunk : *headers) 457 { 458 std::string hdr{headerChunk.dump( 459 -1, ' ', true, nlohmann::json::error_handler_t::replace)}; 460 cumulativeLen += hdr.length(); 461 462 // This value is selected to mirror http_connection.hpp 463 constexpr const uint16_t maxHeaderSizeED = 8096; 464 if (cumulativeLen > maxHeaderSizeED) 465 { 466 messages::arraySizeTooLong(asyncResp->res, "HttpHeaders", 467 maxHeaderSizeED); 468 return; 469 } 470 for (const auto& item : headerChunk.items()) 471 { 472 const std::string* value = 473 item.value().get_ptr<const std::string*>(); 474 if (value == nullptr) 475 { 476 messages::propertyValueFormatError( 477 asyncResp->res, item.value(), 478 "HttpHeaders/" + item.key()); 479 return; 480 } 481 subValue->httpHeaders.set(item.key(), *value); 482 } 483 } 484 } 485 486 if (regPrefixes) 487 { 488 for (const std::string& it : *regPrefixes) 489 { 490 if (std::ranges::find(supportedRegPrefixes, it) == 491 supportedRegPrefixes.end()) 492 { 493 messages::propertyValueNotInList(asyncResp->res, it, 494 "RegistryPrefixes"); 495 return; 496 } 497 } 498 subValue->registryPrefixes = *regPrefixes; 499 } 500 501 if (resTypes) 502 { 503 for (const std::string& it : *resTypes) 504 { 505 if (std::ranges::find(supportedResourceTypes, it) == 506 supportedResourceTypes.end()) 507 { 508 messages::propertyValueNotInList(asyncResp->res, it, 509 "ResourceTypes"); 510 return; 511 } 512 } 513 subValue->resourceTypes = *resTypes; 514 } 515 516 if (msgIds) 517 { 518 std::vector<std::string> registryPrefix; 519 520 // If no registry prefixes are mentioned, consider all 521 // supported prefixes 522 if (subValue->registryPrefixes.empty()) 523 { 524 registryPrefix.assign(supportedRegPrefixes.begin(), 525 supportedRegPrefixes.end()); 526 } 527 else 528 { 529 registryPrefix = subValue->registryPrefixes; 530 } 531 532 for (const std::string& id : *msgIds) 533 { 534 bool validId = false; 535 536 // Check for Message ID in each of the selected Registry 537 for (const std::string& it : registryPrefix) 538 { 539 const std::span<const redfish::registries::MessageEntry> 540 registry = 541 redfish::registries::getRegistryFromPrefix(it); 542 543 if (std::ranges::any_of( 544 registry, 545 [&id](const redfish::registries::MessageEntry& 546 messageEntry) { 547 return id == messageEntry.first; 548 })) 549 { 550 validId = true; 551 break; 552 } 553 } 554 555 if (!validId) 556 { 557 messages::propertyValueNotInList(asyncResp->res, id, 558 "MessageIds"); 559 return; 560 } 561 } 562 563 subValue->registryMsgIds = *msgIds; 564 } 565 566 if (retryPolicy) 567 { 568 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == 569 supportedRetryPolicies.end()) 570 { 571 messages::propertyValueNotInList(asyncResp->res, *retryPolicy, 572 "DeliveryRetryPolicy"); 573 return; 574 } 575 subValue->retryPolicy = *retryPolicy; 576 } 577 else 578 { 579 // Default "TerminateAfterRetries" 580 subValue->retryPolicy = "TerminateAfterRetries"; 581 } 582 583 if (mrdJsonArray) 584 { 585 for (nlohmann::json& mrdObj : *mrdJsonArray) 586 { 587 std::string mrdUri; 588 589 if (!json_util::readJson(mrdObj, asyncResp->res, "@odata.id", 590 mrdUri)) 591 592 { 593 return; 594 } 595 subValue->metricReportDefinitions.emplace_back(mrdUri); 596 } 597 } 598 599 std::string id = 600 EventServiceManager::getInstance().addSubscription(subValue); 601 if (id.empty()) 602 { 603 messages::internalError(asyncResp->res); 604 return; 605 } 606 607 messages::created(asyncResp->res); 608 asyncResp->res.addHeader( 609 "Location", "/redfish/v1/EventService/Subscriptions/" + id); 610 }); 611 } 612 613 inline void requestRoutesEventDestination(App& app) 614 { 615 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 616 .privileges(redfish::privileges::getEventDestination) 617 .methods(boost::beast::http::verb::get)( 618 [&app](const crow::Request& req, 619 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 620 const std::string& param) { 621 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 622 { 623 return; 624 } 625 626 if (param.starts_with("snmp")) 627 { 628 getSnmpTrapClient(asyncResp, param); 629 return; 630 } 631 632 std::shared_ptr<Subscription> subValue = 633 EventServiceManager::getInstance().getSubscription(param); 634 if (subValue == nullptr) 635 { 636 asyncResp->res.result(boost::beast::http::status::not_found); 637 return; 638 } 639 const std::string& id = param; 640 641 asyncResp->res.jsonValue["@odata.type"] = 642 "#EventDestination.v1_8_0.EventDestination"; 643 asyncResp->res.jsonValue["Protocol"] = "Redfish"; 644 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 645 "/redfish/v1/EventService/Subscriptions/{}", id); 646 asyncResp->res.jsonValue["Id"] = id; 647 asyncResp->res.jsonValue["Name"] = "Event Destination " + id; 648 asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl; 649 asyncResp->res.jsonValue["Context"] = subValue->customText; 650 asyncResp->res.jsonValue["SubscriptionType"] = 651 subValue->subscriptionType; 652 asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array(); 653 asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType; 654 asyncResp->res.jsonValue["RegistryPrefixes"] = 655 subValue->registryPrefixes; 656 asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes; 657 658 asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; 659 asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; 660 661 nlohmann::json::array_t mrdJsonArray; 662 for (const auto& mdrUri : subValue->metricReportDefinitions) 663 { 664 nlohmann::json::object_t mdr; 665 mdr["@odata.id"] = mdrUri; 666 mrdJsonArray.emplace_back(std::move(mdr)); 667 } 668 asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray; 669 }); 670 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 671 // The below privilege is wrong, it should be ConfigureManager OR 672 // ConfigureSelf 673 // https://github.com/openbmc/bmcweb/issues/220 674 //.privileges(redfish::privileges::patchEventDestination) 675 .privileges({{"ConfigureManager"}}) 676 .methods(boost::beast::http::verb::patch)( 677 [&app](const crow::Request& req, 678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 679 const std::string& param) { 680 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 681 { 682 return; 683 } 684 std::shared_ptr<Subscription> subValue = 685 EventServiceManager::getInstance().getSubscription(param); 686 if (subValue == nullptr) 687 { 688 asyncResp->res.result(boost::beast::http::status::not_found); 689 return; 690 } 691 692 std::optional<std::string> context; 693 std::optional<std::string> retryPolicy; 694 std::optional<std::vector<nlohmann::json>> headers; 695 696 if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context, 697 "DeliveryRetryPolicy", retryPolicy, 698 "HttpHeaders", headers)) 699 { 700 return; 701 } 702 703 if (context) 704 { 705 subValue->customText = *context; 706 } 707 708 if (headers) 709 { 710 boost::beast::http::fields fields; 711 for (const nlohmann::json& headerChunk : *headers) 712 { 713 for (const auto& it : headerChunk.items()) 714 { 715 const std::string* value = 716 it.value().get_ptr<const std::string*>(); 717 if (value == nullptr) 718 { 719 messages::propertyValueFormatError( 720 asyncResp->res, it.value(), 721 "HttpHeaders/" + it.key()); 722 return; 723 } 724 fields.set(it.key(), *value); 725 } 726 } 727 subValue->httpHeaders = fields; 728 } 729 730 if (retryPolicy) 731 { 732 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == 733 supportedRetryPolicies.end()) 734 { 735 messages::propertyValueNotInList(asyncResp->res, *retryPolicy, 736 "DeliveryRetryPolicy"); 737 return; 738 } 739 subValue->retryPolicy = *retryPolicy; 740 } 741 742 EventServiceManager::getInstance().updateSubscriptionData(); 743 }); 744 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 745 // The below privilege is wrong, it should be ConfigureManager OR 746 // ConfigureSelf 747 // https://github.com/openbmc/bmcweb/issues/220 748 //.privileges(redfish::privileges::deleteEventDestination) 749 .privileges({{"ConfigureManager"}}) 750 .methods(boost::beast::http::verb::delete_)( 751 [&app](const crow::Request& req, 752 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 753 const std::string& param) { 754 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 755 { 756 return; 757 } 758 759 if (param.starts_with("snmp")) 760 { 761 deleteSnmpTrapClient(asyncResp, param); 762 EventServiceManager::getInstance().deleteSubscription(param); 763 return; 764 } 765 766 if (!EventServiceManager::getInstance().isSubscriptionExist(param)) 767 { 768 asyncResp->res.result(boost::beast::http::status::not_found); 769 return; 770 } 771 EventServiceManager::getInstance().deleteSubscription(param); 772 }); 773 } 774 775 } // namespace redfish 776