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<bool> verifyCertificate; 282 std::optional<std::string> context; 283 std::optional<std::string> subscriptionType; 284 std::optional<std::string> eventFormatType2; 285 std::optional<std::string> retryPolicy; 286 std::optional<std::vector<std::string>> msgIds; 287 std::optional<std::vector<std::string>> regPrefixes; 288 std::optional<std::vector<std::string>> resTypes; 289 std::optional<std::vector<nlohmann::json::object_t>> headers; 290 std::optional<std::vector<nlohmann::json::object_t>> mrdJsonArray; 291 292 if (!json_util::readJsonPatch( 293 req, asyncResp->res, "Destination", destUrl, "Context", context, 294 "Protocol", protocol, "SubscriptionType", subscriptionType, 295 "EventFormatType", eventFormatType2, "HttpHeaders", headers, 296 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, 297 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", 298 mrdJsonArray, "ResourceTypes", resTypes, "VerifyCertificate", 299 verifyCertificate)) 300 { 301 return; 302 } 303 304 // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers 305 static constexpr const uint16_t maxDestinationSize = 2000; 306 if (destUrl.size() > maxDestinationSize) 307 { 308 messages::stringValueTooLong(asyncResp->res, "Destination", 309 maxDestinationSize); 310 return; 311 } 312 313 if (regPrefixes && msgIds) 314 { 315 if (!regPrefixes->empty() && !msgIds->empty()) 316 { 317 messages::propertyValueConflict(asyncResp->res, "MessageIds", 318 "RegistryPrefixes"); 319 return; 320 } 321 } 322 323 boost::system::result<boost::urls::url> url = 324 boost::urls::parse_absolute_uri(destUrl); 325 if (!url) 326 { 327 BMCWEB_LOG_WARNING("Failed to validate and split destination url"); 328 messages::propertyValueFormatError(asyncResp->res, destUrl, 329 "Destination"); 330 return; 331 } 332 url->normalize(); 333 crow::utility::setProtocolDefaults(*url, protocol); 334 crow::utility::setPortDefaults(*url); 335 336 if (url->path().empty()) 337 { 338 url->set_path("/"); 339 } 340 341 if (url->has_userinfo()) 342 { 343 messages::propertyValueFormatError(asyncResp->res, destUrl, 344 "Destination"); 345 return; 346 } 347 348 if (protocol == "SNMPv2c") 349 { 350 if (context) 351 { 352 messages::propertyValueConflict(asyncResp->res, "Context", 353 "Protocol"); 354 return; 355 } 356 if (eventFormatType2) 357 { 358 messages::propertyValueConflict(asyncResp->res, 359 "EventFormatType", "Protocol"); 360 return; 361 } 362 if (retryPolicy) 363 { 364 messages::propertyValueConflict(asyncResp->res, "RetryPolicy", 365 "Protocol"); 366 return; 367 } 368 if (msgIds) 369 { 370 messages::propertyValueConflict(asyncResp->res, "MessageIds", 371 "Protocol"); 372 return; 373 } 374 if (regPrefixes) 375 { 376 messages::propertyValueConflict(asyncResp->res, 377 "RegistryPrefixes", "Protocol"); 378 return; 379 } 380 if (resTypes) 381 { 382 messages::propertyValueConflict(asyncResp->res, "ResourceTypes", 383 "Protocol"); 384 return; 385 } 386 if (headers) 387 { 388 messages::propertyValueConflict(asyncResp->res, "HttpHeaders", 389 "Protocol"); 390 return; 391 } 392 if (mrdJsonArray) 393 { 394 messages::propertyValueConflict( 395 asyncResp->res, "MetricReportDefinitions", "Protocol"); 396 return; 397 } 398 if (url->scheme() != "snmp") 399 { 400 messages::propertyValueConflict(asyncResp->res, "Destination", 401 "Protocol"); 402 return; 403 } 404 405 addSnmpTrapClient(asyncResp, url->host_address(), 406 url->port_number()); 407 return; 408 } 409 410 std::shared_ptr<Subscription> subValue = 411 std::make_shared<Subscription>(*url, app.ioContext()); 412 413 subValue->destinationUrl = std::move(*url); 414 415 if (subscriptionType) 416 { 417 if (*subscriptionType != "RedfishEvent") 418 { 419 messages::propertyValueNotInList( 420 asyncResp->res, *subscriptionType, "SubscriptionType"); 421 return; 422 } 423 subValue->subscriptionType = *subscriptionType; 424 } 425 else 426 { 427 subValue->subscriptionType = "RedfishEvent"; // Default 428 } 429 430 if (protocol != "Redfish") 431 { 432 messages::propertyValueNotInList(asyncResp->res, protocol, 433 "Protocol"); 434 return; 435 } 436 subValue->protocol = protocol; 437 438 if (verifyCertificate) 439 { 440 subValue->verifyCertificate = *verifyCertificate; 441 } 442 443 if (eventFormatType2) 444 { 445 if (std::ranges::find(supportedEvtFormatTypes, *eventFormatType2) == 446 supportedEvtFormatTypes.end()) 447 { 448 messages::propertyValueNotInList( 449 asyncResp->res, *eventFormatType2, "EventFormatType"); 450 return; 451 } 452 subValue->eventFormatType = *eventFormatType2; 453 } 454 else 455 { 456 // If not specified, use default "Event" 457 subValue->eventFormatType = "Event"; 458 } 459 460 if (context) 461 { 462 // This value is selected arbitrarily. 463 constexpr const size_t maxContextSize = 256; 464 if (context->size() > maxContextSize) 465 { 466 messages::stringValueTooLong(asyncResp->res, "Context", 467 maxContextSize); 468 return; 469 } 470 subValue->customText = *context; 471 } 472 473 if (headers) 474 { 475 size_t cumulativeLen = 0; 476 477 for (const nlohmann::json::object_t& headerChunk : *headers) 478 { 479 for (const auto& item : headerChunk) 480 { 481 const std::string* value = 482 item.second.get_ptr<const std::string*>(); 483 if (value == nullptr) 484 { 485 messages::propertyValueFormatError( 486 asyncResp->res, item.second, 487 "HttpHeaders/" + item.first); 488 return; 489 } 490 // Adding a new json value is the size of the key, + 491 // the size of the value + 2 * 2 quotes for each, + 492 // the colon and space between. example: 493 // "key": "value" 494 cumulativeLen += item.first.size() + value->size() + 6; 495 // This value is selected to mirror http_connection.hpp 496 constexpr const uint16_t maxHeaderSizeED = 8096; 497 if (cumulativeLen > maxHeaderSizeED) 498 { 499 messages::arraySizeTooLong( 500 asyncResp->res, "HttpHeaders", maxHeaderSizeED); 501 return; 502 } 503 subValue->httpHeaders.set(item.first, *value); 504 } 505 } 506 } 507 508 if (regPrefixes) 509 { 510 for (const std::string& it : *regPrefixes) 511 { 512 if (std::ranges::find(supportedRegPrefixes, it) == 513 supportedRegPrefixes.end()) 514 { 515 messages::propertyValueNotInList(asyncResp->res, it, 516 "RegistryPrefixes"); 517 return; 518 } 519 } 520 subValue->registryPrefixes = *regPrefixes; 521 } 522 523 if (resTypes) 524 { 525 for (const std::string& it : *resTypes) 526 { 527 if (std::ranges::find(supportedResourceTypes, it) == 528 supportedResourceTypes.end()) 529 { 530 messages::propertyValueNotInList(asyncResp->res, it, 531 "ResourceTypes"); 532 return; 533 } 534 } 535 subValue->resourceTypes = *resTypes; 536 } 537 538 if (msgIds) 539 { 540 std::vector<std::string> registryPrefix; 541 542 // If no registry prefixes are mentioned, consider all 543 // supported prefixes 544 if (subValue->registryPrefixes.empty()) 545 { 546 registryPrefix.assign(supportedRegPrefixes.begin(), 547 supportedRegPrefixes.end()); 548 } 549 else 550 { 551 registryPrefix = subValue->registryPrefixes; 552 } 553 554 for (const std::string& id : *msgIds) 555 { 556 bool validId = false; 557 558 // Check for Message ID in each of the selected Registry 559 for (const std::string& it : registryPrefix) 560 { 561 const std::span<const redfish::registries::MessageEntry> 562 registry = 563 redfish::registries::getRegistryFromPrefix(it); 564 565 if (std::ranges::any_of( 566 registry, 567 [&id](const redfish::registries::MessageEntry& 568 messageEntry) { 569 return id == messageEntry.first; 570 })) 571 { 572 validId = true; 573 break; 574 } 575 } 576 577 if (!validId) 578 { 579 messages::propertyValueNotInList(asyncResp->res, id, 580 "MessageIds"); 581 return; 582 } 583 } 584 585 subValue->registryMsgIds = *msgIds; 586 } 587 588 if (retryPolicy) 589 { 590 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == 591 supportedRetryPolicies.end()) 592 { 593 messages::propertyValueNotInList(asyncResp->res, *retryPolicy, 594 "DeliveryRetryPolicy"); 595 return; 596 } 597 subValue->retryPolicy = *retryPolicy; 598 } 599 else 600 { 601 // Default "TerminateAfterRetries" 602 subValue->retryPolicy = "TerminateAfterRetries"; 603 } 604 605 if (mrdJsonArray) 606 { 607 for (nlohmann::json::object_t& mrdObj : *mrdJsonArray) 608 { 609 std::string mrdUri; 610 611 if (!json_util::readJsonObject(mrdObj, asyncResp->res, 612 "@odata.id", mrdUri)) 613 614 { 615 return; 616 } 617 subValue->metricReportDefinitions.emplace_back(mrdUri); 618 } 619 } 620 621 std::string id = 622 EventServiceManager::getInstance().addSubscription(subValue); 623 if (id.empty()) 624 { 625 messages::internalError(asyncResp->res); 626 return; 627 } 628 629 messages::created(asyncResp->res); 630 asyncResp->res.addHeader( 631 "Location", "/redfish/v1/EventService/Subscriptions/" + id); 632 }); 633 } 634 635 inline void requestRoutesEventDestination(App& app) 636 { 637 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 638 .privileges(redfish::privileges::getEventDestination) 639 .methods(boost::beast::http::verb::get)( 640 [&app](const crow::Request& req, 641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 642 const std::string& param) { 643 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 644 { 645 return; 646 } 647 648 if (param.starts_with("snmp")) 649 { 650 getSnmpTrapClient(asyncResp, param); 651 return; 652 } 653 654 std::shared_ptr<Subscription> subValue = 655 EventServiceManager::getInstance().getSubscription(param); 656 if (subValue == nullptr) 657 { 658 asyncResp->res.result(boost::beast::http::status::not_found); 659 return; 660 } 661 const std::string& id = param; 662 663 asyncResp->res.jsonValue["@odata.type"] = 664 "#EventDestination.v1_8_0.EventDestination"; 665 asyncResp->res.jsonValue["Protocol"] = "Redfish"; 666 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 667 "/redfish/v1/EventService/Subscriptions/{}", id); 668 asyncResp->res.jsonValue["Id"] = id; 669 asyncResp->res.jsonValue["Name"] = "Event Destination " + id; 670 asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl; 671 asyncResp->res.jsonValue["Context"] = subValue->customText; 672 asyncResp->res.jsonValue["SubscriptionType"] = 673 subValue->subscriptionType; 674 asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array(); 675 asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType; 676 asyncResp->res.jsonValue["RegistryPrefixes"] = 677 subValue->registryPrefixes; 678 asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes; 679 680 asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; 681 asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; 682 asyncResp->res.jsonValue["VerifyCertificate"] = 683 subValue->verifyCertificate; 684 685 nlohmann::json::array_t mrdJsonArray; 686 for (const auto& mdrUri : subValue->metricReportDefinitions) 687 { 688 nlohmann::json::object_t mdr; 689 mdr["@odata.id"] = mdrUri; 690 mrdJsonArray.emplace_back(std::move(mdr)); 691 } 692 asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray; 693 }); 694 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 695 // The below privilege is wrong, it should be ConfigureManager OR 696 // ConfigureSelf 697 // https://github.com/openbmc/bmcweb/issues/220 698 //.privileges(redfish::privileges::patchEventDestination) 699 .privileges({{"ConfigureManager"}}) 700 .methods(boost::beast::http::verb::patch)( 701 [&app](const crow::Request& req, 702 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 703 const std::string& param) { 704 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 705 { 706 return; 707 } 708 std::shared_ptr<Subscription> subValue = 709 EventServiceManager::getInstance().getSubscription(param); 710 if (subValue == nullptr) 711 { 712 asyncResp->res.result(boost::beast::http::status::not_found); 713 return; 714 } 715 716 std::optional<std::string> context; 717 std::optional<std::string> retryPolicy; 718 std::optional<bool> verifyCertificate; 719 std::optional<std::vector<nlohmann::json::object_t>> headers; 720 721 if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context, 722 "VerifyCertificate", verifyCertificate, 723 "DeliveryRetryPolicy", retryPolicy, 724 "HttpHeaders", headers)) 725 { 726 return; 727 } 728 729 if (context) 730 { 731 subValue->customText = *context; 732 } 733 734 if (headers) 735 { 736 boost::beast::http::fields fields; 737 for (const nlohmann::json::object_t& headerChunk : *headers) 738 { 739 for (const auto& it : headerChunk) 740 { 741 const std::string* value = 742 it.second.get_ptr<const std::string*>(); 743 if (value == nullptr) 744 { 745 messages::propertyValueFormatError( 746 asyncResp->res, it.second, 747 "HttpHeaders/" + it.first); 748 return; 749 } 750 fields.set(it.first, *value); 751 } 752 } 753 subValue->httpHeaders = std::move(fields); 754 } 755 756 if (retryPolicy) 757 { 758 if (std::ranges::find(supportedRetryPolicies, *retryPolicy) == 759 supportedRetryPolicies.end()) 760 { 761 messages::propertyValueNotInList(asyncResp->res, *retryPolicy, 762 "DeliveryRetryPolicy"); 763 return; 764 } 765 subValue->retryPolicy = *retryPolicy; 766 } 767 768 if (verifyCertificate) 769 { 770 subValue->verifyCertificate = *verifyCertificate; 771 } 772 773 EventServiceManager::getInstance().updateSubscriptionData(); 774 }); 775 BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/") 776 // The below privilege is wrong, it should be ConfigureManager OR 777 // ConfigureSelf 778 // https://github.com/openbmc/bmcweb/issues/220 779 //.privileges(redfish::privileges::deleteEventDestination) 780 .privileges({{"ConfigureManager"}}) 781 .methods(boost::beast::http::verb::delete_)( 782 [&app](const crow::Request& req, 783 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 784 const std::string& param) { 785 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 786 { 787 return; 788 } 789 790 if (param.starts_with("snmp")) 791 { 792 deleteSnmpTrapClient(asyncResp, param); 793 EventServiceManager::getInstance().deleteSubscription(param); 794 return; 795 } 796 797 if (!EventServiceManager::getInstance().isSubscriptionExist(param)) 798 { 799 asyncResp->res.result(boost::beast::http::status::not_found); 800 return; 801 } 802 EventServiceManager::getInstance().deleteSubscription(param); 803 }); 804 } 805 806 } // namespace redfish 807