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