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