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